#!/bin/sh
#
# Copyright (c) 2021 Omar Polo <op@omarpolo.com>
#
# 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 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.

# adapted from mandoc configure and oconfigure, thanks schwarze@ and
# kristaps@!

set -e

[ -w config.log ] && mv config.log config.log.old
[ -w config.h   ] && mv config.h   config.h.old

# Output file descriptor usage:
# 1 (stdout): config.h, Makefile.local
# 2 (stderr): original stderr, usually to the console
# 3: config.log

exec 3> config.log
echo "file config.log: writing..."

# --------
# default settings: initialize all vars here such that nothing is
# leaked from the environment except for CC, CFLAGS and LDFLAGS

VERSION=1.8.3

CC=`printf "all:\\n\\t@echo \\\$(CC)\\n" | make ${MAKE_FLAGS} -sf -`

if [ -z "${CFLAGS}" ]; then
	CFLAGS=`printf "all:\\n\\t@echo \\\$(CFLAGS)\\n" | make ${MAKE_FLAGS} -sf -`
fi

CFLAGS="${CFLAGS} -W -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes"
CFLAGS="${CFLAGS} -Wwrite-strings -Wno-unused-parameter"

if [ -z "${LDFLAGS}" ]; then
	LDFLAGS=`printf "all:\\n\\t@echo \\\$(LDFLAGS)\\n" | make ${MAKE_FLAGS} -sf -`
	LDFLAGS="-ltls -levent -lcrypto"
fi

LD_IMSG=
STATIC=
YACC=yacc

DISABLE_SANDBOX=0

NEED_GNU_SOURCE=0
NEED_OPENBSD_SOURCE=0

PREFIX="/usr/local"
BINDIR=

INSTALL="install"

add_library() {
	if pkg-config "$1"; then
		CFLAGS="${CFLAGS} $(pkg-config --cflags "$1")"
		LDFLAGS="${LDFLAGS} $(pkg-config --libs "$1")"
	fi
}

# try to auto detect CFLAGS and LDFLAGS
if command -v pkg-config >/dev/null; then
	add_library "libtls"
	add_library "openssl"

	case "$(uname)" in
		OpenBSD)
			# use libevent and imsg in base
			;;
		*)
			add_library "libevent"
			add_library "libimsg"
			;;
	esac

	case "$(uname)" in
		*BSD|DragonFly|Darwin)
			;;
		*)
			add_library "libbsd-ctor libbsd-overlay"
			;;
	esac
fi

# auto detect yacc/bison
command -v ${YACC} >/dev/null || {
	echo "${YACC} not found: trying bison" 1>&2
	echo "${YACC} not found: trying bison" 1>&3
	YACC=bison
	command -v ${YACC} >/dev/null || {
		echo "${YACC} not found: giving up" 1>&2
		echo "${YACC} not found: giving up" 1>&3
	}
}

# --------
# allow certain variables to be overridden on the command line

for keyvals in "$@"; do
	if [ "$keyvals" = "--disable-sandbox" ]; then
		DISABLE_SANDBOX=1
		continue
	fi

	if [ "$keyvals" = "--enable-sandbox" ]; then
		DISABLE_SANDBOX=0
		continue
	fi

	key=`echo $keyvals | cut -s -d '=' -f1`
	if [ -z "$key" ]; then
		echo "$0: invalid key-value: $keyvals" 1>&2
		exit 1
	fi
	val=`echo $keyvals | cut -d '=' -f 2-`
	case "$key" in
		BINDIR)   BINDIR="$val" ;;
		CC)       CC="$val" ;;
		CFLAGS)   CFLAGS="$val" ;;
		DESTDIR)  DESTDIR="$val" ;;
		LDFLAGS)  LDFLAGS="$val" ;;
		MANDIR)   MANDIR="$val" ;;
		PREFIX)   PREFIX="$val" ;;
		YACC)     YACC="$val" ;;
		--prefix) PREFIX="$val" ;;
		*)
			echo "$0: invalid key: $key" 1>&2
			exit 1
	esac
done

# --------
# tests functions

# Check whether this HAVE_ setting is manually overridden.
# If yes, use the override, if no, do not decide anything yet.
# Arguments: test file name, test var name, manual value
ismanual() {
	[ -z "${3}" ] && return 1
	echo "tested ${1}: HAVE_${2}=${3} (manual)" 1>&2
	echo "tested ${1}: HAVE_${2}=${3} (manual)" 1>&3
	echo 1>&3
	return 0
}

# Run a single autoconfiguration test.
# In case of success, enable the feature.
# In case of failure, do not decide anything yet.
# Arguments: test file name, test var name, additional CFLAGS
singletest() {
	n=${1}${3}
	cat 1>&3 << __HEREDOC__
testing ${n} ...
${COMP} -o have/${1} have/${1}.c ${3} ${LDFLAGS}
__HEREDOC__

	if ${COMP} -o "have/${1}" "have/${1}.c" ${3} ${LDFLAGS} 1>&3 2>&3
	then
		echo "tested ${n}: yes" 1>&2
		echo "tested ${n}: yes" 1>&3
		echo 1>&3
		eval HAVE_${2}=1
		[ "${3}" = "-D_GNU_SOURCE" ] && NEED_GNU_SOURCE=1
		[ "${3}" = "-D_OPENBSD_SOURCE" ] && NEED_OPENBSD_SOURCE=1
		[ "${3}" = "-lutil" ] && LD_IMSG="-lutil"
		rm "have/${1}"
		return 0
	else
		echo "tested ${n}: no (compilation failed)" 1>&2
		echo "result of ${n}: ${CC} failed with exit status $?" 1>&3
		echo "result of compiling ${n}: no" 1>&3
		echo 1>&3
		return 1
	fi
}

# Run a complete autoconfiguration test, including the check for
# a manual override and disabling the feature on failure.
# Arguments: test file name, test var name, additional CFLAGS
# The final argument can optionally be repeated a second time.
runtest() {
	eval _manual=\${HAVE_${2}}
	ismanual "${1}" "${2}" "${_manual}" && return 0
	singletest "${1}" "${2}" "${3}" && return 0
	[ -n "${4}" ] && singletest "${1}" "${2}" "${4}" && return 0
	eval HAVE_${2}=0
	return 1
}

# --------
# compiler options

COMP="${CC} ${CFLAGS} -Wno-unused -Werror"

echo "selected CFLAGS=\"${CFLAGS}\"" 1>&2
echo "selected CFLAGS=\"${CFLAGS}\"" 1>&3
echo 1>&3

if [ -n "${STATIC}" ]; then
	echo "selected STATIC=\"${STATIC}\" (manual)" 1>&2
	echo "selected STATIC=\"${STATIC}\" (manual)" 1>&3
	echo 1>&3
else
	runtest noop STATIC -static || true
	[ ${HAVE_STATIC} -eq 0 ] || STATIC="-static"
	echo "selected STATIC=\"${STATIC}\"" 1>&2
	echo "selected STATIC=\"${STATIC}\"" 1>&3
	echo 1>&3
fi

if runtest noop MMD -MMD; then
	CFLAGS="${CFLAGS} -MMD"
	echo "adding -MMD to CFLAGS" 1>&2
	echo "adding -MMD to CFLAGS" 1>&3
fi

# --------
# tests for config.h

runtest err		ERR		|| true
runtest explicit_bzero	EXPLICIT_BZERO	|| true
runtest freezero	FREEZERO	|| true
runtest getdtablecount	GETDTABLECOUNT	|| true
runtest getdtablesize	GETDTABLESIZE	|| true
runtest getprogname	GETPROGNAME	|| true
runtest imsg		IMSG		-lutil || true
runtest landlock	LANDLOCK	|| true
runtest libevent	LIBEVENT	|| true
runtest libevent2	LIBEVENT2	|| true
runtest libtls		LIBTLS		|| true
runtest openssl		OPENSSL		|| true
runtest pr_set_name	PR_SET_NAME	|| true
runtest program_invocation_short_name PROGRAM_INVOCATION_SHORT_NAME "" -D_GNU_SOURCE || true
runtest queue_h		QUEUE_H		|| true
runtest reallocarray	REALLOCARRAY	|| true
runtest recallocarray	RECALLOCARRAY	|| true
runtest setproctitle	SETPROCTITLE	|| true
runtest strlcat		STRLCAT		|| true
runtest strlcpy		STRLCPY		|| true
runtest strtonum	STRTONUM	|| true
runtest tree_h		TREE_H		|| true
runtest vasprintf	VASPRINTF	"" -D_GNU_SOURCE || true

if [ ${HAVE_LIBTLS} -eq 0 ]; then
	echo "FATAL: libtls not found" 1>&2
	echo "FATAL: libtls not found" 1>&3
	exit 1
fi

if [ ${HAVE_OPENSSL} -eq 0 ]; then
	echo "FATAL: openssl not found" 1>&2
	echo "FATAL: openssl not found" 1>&3
	exit 1
fi

if [ ${HAVE_LIBEVENT} -eq 0 ]; then
	echo "FATAL: libevent not found" 1>&2
	echo "FATAL: libevent not found" 1>&3
	exit 1
fi

if [ ${HAVE_QUEUE_H} -eq 0 -o ${HAVE_IMSG} -eq 0 -o ${HAVE_TREE_H} -eq 0 ]; then
	CFLAGS="${CFLAGS} -I ${PWD}/compat"
fi

# --------
# write config.h

exec > config.h

cat <<__HEREDOC__
#ifdef __cplusplus
#error "Do not use C++."
#endif
__HEREDOC__

[ ${NEED_GNU_SOURCE} -eq 0 ] || echo "#define _GNU_SOURCE"
[ ${NEED_OPENBSD_SOURCE} -eq 0 ] || echo "#define _OPENBSD_SOURCE"

[ ${HAVE_STRLCAT} -eq 0 -o ${HAVE_STRLCPY} -eq 0 -o ${HAVE_IMSG} -eq 0 ] \
	&& echo "#include <sys/types.h>"
[ ${HAVE_VASPRINTF} -eq 0 ] && echo "#include <stdarg.h>"

if [ ${HAVE_QUEUE_H} -eq 1 ]; then
	echo "#include <sys/queue.h>"
else
	echo "#include <queue.h>"
fi

if [ ${HAVE_TREE_H} -eq 1 ]; then
	echo "#include <sys/tree.h>"
else
	echo "#include <tree.h>"
fi

echo "#include <sys/types.h>"
echo "#include <sys/uio.h>"
echo "#include <stdint.h>"
echo "#include <imsg.h>"

cat <<__HEREDOC__

#define VERSION			"${VERSION}"
#define DISABLE_SANDBOX		${DISABLE_SANDBOX}

#define HAVE_ERR		${HAVE_ERR}
#define HAVE_EXPLICIT_BZERO	${HAVE_EXPLICIT_BZERO}
#define HAVE_FREEZERO		${HAVE_FREEZERO}
#define HAVE_GETDTABLECOUNT	${HAVE_GETDTABLECOUNT}
#define HAVE_GETDTABLESIZE	${HAVE_GETDTABLESIZE}
#define HAVE_GETPROGNAME	${HAVE_GETPROGNAME}
#define HAVE_IMSG		${HAVE_IMSG}
#define HAVE_LANDLOCK		${HAVE_LANDLOCK}
#define HAVE_LIBEVENT		${HAVE_LIBEVENT}
#define HAVE_LIBEVENT2		${HAVE_LIBEVENT2}
#define HAVE_PROGRAM_INVOCATION_SHORT_NAME ${HAVE_PROGRAM_INVOCATION_SHORT_NAME}
#define HAVE_PR_SET_NAME	${HAVE_PR_SET_NAME}
#define HAVE_QUEUE_H		${HAVE_QUEUE_H}
#define HAVE_REALLOCARRAY	${HAVE_REALLOCARRAY}
#define HAVE_RECALLOCARRAY	${HAVE_RECALLOCARRAY}
#define HAVE_SETPROCTITLE	${HAVE_SETPROCTITLE}
#define HAVE_STRLCAT		${HAVE_STRLCAT}
#define HAVE_STRLCPY		${HAVE_STRLCPY}
#define HAVE_STRTONUM		${HAVE_STRTONUM}
#define HAVE_TREE_H		${HAVE_TREE_H}
#define HAVE_VASPRINTF		${HAVE_VASPRINTF}

__HEREDOC__

[ ${HAVE_EXPLICIT_BZERO} -eq 0 -o \
  ${HAVE_FREEZERO} -eq 0 -o \
  ${HAVE_REALLOCARRAY} -eq 0 -o \
  ${HAVE_RECALLOCARRAY} -eq 0 -o \
  ${HAVE_STRLCAT} -eq 0 -o \
  ${HAVE_STRLCPY} -eq 0 -o \
  ${HAVE_STRTONUM} -eq 0 ] && echo "#include <stddef.h>"

if [ ${HAVE_ERR} -eq 0 ]; then
	echo "extern	void	 err(int, const char*, ...);"
	echo "extern	void	 errx(int, const char*, ...);"
	echo "extern	void	 warn(const char*, ...);"
	echo "extern	void	 warnx(const char*, ...);"
	COMPAT="${COMPAT} compat/err.o"
else
	echo "#include <err.h>"
fi
if [ ${HAVE_EXPLICIT_BZERO} -eq 0 ]; then
	echo "extern	void	 explicit_bzero(void*, size_t);"
	COMPAT="${COMPAT} compat/explicit_bzero.o"
fi
if [ ${HAVE_FREEZERO} -eq 0 ]; then
	echo "extern	void	 freezero(void*, size_t);"
	COMPAT="${COMPAT} compat/freezero.o"
fi
if [ ${HAVE_GETDTABLECOUNT} -eq 0 ]; then
	echo "extern	int	 getdtablecount(void);"
	COMPAT="${COMPAT} compat/getdtablecount.o"
fi
if [ ${HAVE_GETDTABLESIZE} -eq 0 ]; then
	echo "extern	int	 getdtablesize(void);"
	COMPAT="${COMPAT} compat/getdtablesize.o"
fi
if [ ${HAVE_GETPROGNAME} -eq 0 ]; then
	echo "extern	const char *getprogname(void);"
	COMPAT="${COMPAT} compat/getprogname.o"
fi
if [ ${HAVE_IMSG} -eq 0 ]; then
	COMPAT="${COMPAT} compat/imsg.o compat/imsg-buffer.o"
fi
if [ ${HAVE_REALLOCARRAY} -eq 0 ]; then
	echo "extern	void	*reallocarray(void*, size_t, size_t);"
	COMPAT="${COMPAT} compat/reallocarray.o"
fi
if [ ${HAVE_RECALLOCARRAY} -eq 0 ]; then
	echo "extern	void	*recallocarray(void*, size_t, size_t, size_t);"
	COMPAT="${COMPAT} compat/recallocarray.o"
fi
if [ ${HAVE_SETPROCTITLE} -eq 0 ]; then
	echo "extern	void	 setproctitle(const char *fmt, ...);"
	COMPAT="${COMPAT} compat/setproctitle.o"
fi
if [ ${HAVE_STRLCAT} -eq 0 ]; then
	echo "extern	size_t	 strlcat(char*, const char*, size_t);"
	COMPAT="${COMPAT} compat/strlcat.o"
fi
if [ ${HAVE_STRLCPY} -eq 0 ]; then
	echo "extern	size_t	 strlcpy(char*, const char*, size_t);"
	COMPAT="${COMPAT} compat/strlcpy.o"
fi
if [ ${HAVE_STRTONUM} -eq 0 ]; then
	echo "extern	long long strtonum(const char*, long long, long long, const char**);"
	COMPAT="${COMPAT} compat/strtonum.o"
fi
if [ ${HAVE_VASPRINTF} -eq 0 ]; then
	echo "extern	int	 vasprintf(char**, const char*, va_list);"
	COMPAT="${COMPAT} compat/vasprintf.o"
fi
echo "file config.h: written" 1>&2
echo "file config.h: written" 1>&3

# --------
# tests for Makefile.local

GMID_SRCS="dirs.c ex.c fcgi.c gmid.c iri.c log.c mime.c proxy.c puny.c"
GMID_SRCS="${GMID_SRCS} sandbox.c server.c utf8.c utils.c y.tab.c"

GMID_OBJS=
for src in ${GMID_SRCS}; do
	GMID_OBJS="${GMID_OBJS} ${src%.c}.o"
done
GMID_OBJS="${GMID_OBJS} ${COMPAT}"

GG_SRCS="gg.c iri.c utf8.c"

GG_OBJS=
for src in ${GG_SRCS}; do
	GG_OBJS="${GG_OBJS} ${src%.c}.o"
done
GG_OBJS="${GG_OBJS} ${COMPAT}"

ALL_SRCS="${GMID_SRCS} gg.c"

exec > Makefile.local

[ -z "${BINDIR}"          ] && BINDIR="${PREFIX}/bin"
[ -z "${MANDIR}"          ] && MANDIR="${PREFIX}/man"

[ -z "${INSTALL_PROGRAM}" ] && INSTALL_PROGRAM="${INSTALL} -m 0555"
[ -z "${INSTALL_LIB}"     ] && INSTALL_LIB="${INSTALL} -m 0444"
[ -z "${INSTALL_MAN}"     ] && INSTALL_MAN="${INSTALL} -m 0444"
[ -z "${INSTALL_DATA}"    ] && INSTALL_DATA="${INSTALL} -m 0444"

cat << __HEREDOC__
CC		= ${CC}
CFLAGS		= ${CFLAGS}
LDFLAGS		= ${LDFLAGS} ${LD_IMSG}
YACC		= ${YACC}
STATIC		= ${STATIC}
PREFIX		= ${PREFIX}
BINDIR		= ${BINDIR}
MANDIR		= ${MANDIR}
INCLUDEDIR	= ${INCLUDEDIR}
INSTALL		= ${INSTALL}
INSTALL_PROGRAM	= ${INSTALL_PROGRAM}
INSTALL_LIB	= ${INSTALL_LIB}
INSTALL_MAN	= ${INSTALL_MAN}
INSTALL_DATA	= ${INSTALL_DATA}

GMID_OBJS	= ${GMID_OBJS}
GG_OBJS		= ${GG_OBJS}

VERSION		= ${VERSION}

__HEREDOC__

printf "COMPAT ="
for comp in ${COMPAT}; do
	printf " %s/%s" "$PWD" "$comp"
done
printf "\n\n"

# Include dependency info
for src in ${ALL_SRCS}; do
	printf "%s\n" "-include ${src%.c}.d"
done

for comp in ${COMPAT}; do
	printf "%s\n" "-include ${comp%.o}.d"
done

echo "file Makefile.local: written" 1>&2
echo "file Makefile.local: written" 1>&3

exit 0
