#!/bin/sh

# Copyright (c) 2011 Tim van der Molen <tim@kariliq.nl>
#
# 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.

# $1: function name
# $2: statements
# $3: headers
# $4: macros
# $5: compiler options
check_function()
{
	if [ "${1#* }" = "$1" ]; then
		print_check "for $1()"
	else
		print_check "for $1"
	fi

	if compile "$4" "$3" "$2" "$5"; then
		print_result yes
		return 0
	else
		print_result no
		return 1
	fi
}

# $1: header
# $2: headers to include first
check_header()
{
	print_check "for <$1>"

	if compile "" "$2 $1"; then
		print_result yes
		return 0
	else
		print_result no
		return 1
	fi
}

check_library()
{
	print_check "for lib$1"

	if compile "" "" "" -l$1; then
		print_result yes
		return 0
	else
		print_result no
		return 1
	fi
}

check_pkgconfig()
{
	print_check "for package $1"

	if run_command pkg-config $1; then
		print_result yes
		return 0
	else
		print_result no
		return 1
	fi
}

# $1: plug-in name
# $2: plug-in type (either "ip" or "op")
# $3: packages to check for
check_plugin_pkgconfig()
{
	local pkg

	eval [ "\$enable_$1" = no ] && return 1

	for pkg in $3; do
		check_pkgconfig $pkg || return 1
	done

	if [ "$2" = ip ]; then
		enabled_ips="$enabled_ips $1"
		makefile_append IP $1
	else
		enabled_ops="$enabled_ops $1"
		makefile_append OP $1
	fi

	makefile_assign CPPFLAGS_$1 "$(pkg-config --cflags $3)"
	makefile_assign LDFLAGS_$1 "$(pkg-config --libs $3)"

	return 0
}

check_program()
{
	print_check "for $1"

	if run_command command -v "$1"; then
		print_result yes
	else
		print_result no
		error "cannot find program $1"
	fi
}

# $1: macros
# $2: headers
# $3: statements
# $4: compiler options
compile()
{
	local header line macro outfile result srcfile

	outfile=configure-test.out
	srcfile=configure-test.c

	rm -f $srcfile

	for macro in $1; do
		echo "#define $macro" >> $srcfile
	done

	echo '#include "config.h"' >> $srcfile

	for header in $2; do
		echo "#include <$header>" >> $srcfile
	done

	echo "int main(void) { $3; return 0; }" >> $srcfile

	echo compiling test program: >> $logfile
	while read -r line; do
		printf "\t%s\n" "$line" >> $logfile
	done < $srcfile

	run_command $CC $CFLAGS $CPPFLAGS -o $outfile $srcfile $LDFLAGS $4
	result=$?

	rm -f $outfile $srcfile
	return $result
}

error()
{
	echo "${0##*/}: $@" >& 2
	printf "\nerror: %s\n" "$@" >> $logfile
	exit 1
}

# $1: the name of the variable to save the value to
# $2: the "option=value" string
get_option_value()
{
	local option value

	option="${2%%=*}"
	value="${2#*=}"

	if [ "$value" = yes -o "$value" = no ]; then
		eval $1=$value
	else
		error "option $option: $value: invalid value"
	fi
}

header_define()
{
	echo "#define $@" >> $header
}

makefile_append()
{
	[ "$2" ] && echo "$1+=$2" >> $makefile
}

makefile_assign()
{
	echo "$1=$2" >> $makefile
}

print_check()
{
	printf "checking %s..." "$@"
	printf "\nchecking %s\n" "$@" >> $logfile
}

print_result()
{
	echo " $@"
	echo "result: $@" >> $logfile
}

run_command()
{
	local status

	echo "running command: $@" >> $logfile
	eval $@ >> $logfile 2>& 1
	status=$?
	echo "exit status: $status" >> $logfile
	return $status
}

logfile=configure.log
header=config.h
makefile=config.mk

prefix=/usr/local
bindir_suffix=bin
mandir_suffix=man
plugindir_suffix=lib/siren

enable_debug=no
enable_aac=yes
enable_ffmpeg=yes
enable_flac=yes
enable_mad=yes
enable_mpg123=yes
enable_opus=yes
enable_sndfile=yes
enable_vorbis=yes
enable_wavpack=yes
enable_alsa=yes
enable_ao=yes
enable_oss=yes
enable_portaudio=yes
enable_pulse=yes
enable_sndio=yes
enable_sun=yes

rm -f $logfile $header $makefile

uname -srvm >> $logfile

printf "\narguments:\n" >> $logfile
if [ $# -eq 0 ]; then
	echo "(none)" >> $logfile
else
	for arg; do
		echo "$arg" >> $logfile
	done
fi

for arg; do
	case "$arg" in
	prefix=*)
		prefix="${arg#*=}"
		;;
	bindir=*)
		bindir="${arg#*=}"
		;;
	mandir=*)
		mandir="${arg#*=}"
		;;
	plugindir=*)
		plugindir="${arg#*=}"
		;;
	debug=*)
		get_option_value enable_debug "$arg"
		;;
	aac=*)
		get_option_value enable_aac "$arg"
		;;
	ffmpeg=*)
		get_option_value enable_ffmpeg "$arg"
		;;
	flac=*)
		get_option_value enable_flac "$arg"
		;;
	mad=*)
		get_option_value enable_mad "$arg"
		;;
	mpg123=*)
		get_option_value enable_mpg123 "$arg"
		;;
	opus=*)
		get_option_value enable_opus "$arg"
		;;
	sndfile=*)
		get_option_value enable_sndfile "$arg"
		;;
	vorbis=*)
		get_option_value enable_vorbis "$arg"
		;;
	wavpack=*)
		get_option_value enable_wavpack "$arg"
		;;
	alsa=*)
		get_option_value enable_alsa "$arg"
		;;
	ao=*)
		get_option_value enable_ao "$arg"
		;;
	oss=*)
		get_option_value enable_oss "$arg"
		;;
	portaudio=*)
		get_option_value enable_portaudio "$arg"
		;;
	pulse=*)
		get_option_value enable_pulse "$arg"
		;;
	sndio=*)
		get_option_value enable_sndio "$arg"
		;;
	sun=*)
		get_option_value enable_sun "$arg"
		;;
	help|-h|--help)
		error "see the INSTALL file for more information"
		;;
	*)
		error "${arg%%=*}: invalid option"
		;;
	esac
done

if [ -z "$CC" ]; then
	CC=cc
else
	makefile_assign CC "$CC"
fi

header_define VERSION "\"$(cat version)\""

makefile_assign CFLAGS "$CFLAGS"
makefile_assign CPPFLAGS "$CPPFLAGS"
makefile_assign LDFLAGS "$LDFLAGS"

if [ "$(uname)" = Darwin ]; then
	makefile_assign LDFLAGS_PROG -lcurses
	makefile_assign LDFLAGS_LIB "-bundle -bundle_loader siren"
else
	makefile_assign LDFLAGS_PROG "-Wl,--export-dynamic -pthread -lcurses"
	makefile_assign CFLAGS_LIB -fPIC
	makefile_assign LDFLAGS_LIB "-fPIC -shared"
fi

[ -z "$bindir" ] && bindir="$prefix/$bindir_suffix"
[ -z "$mandir" ] && mandir="$prefix/$mandir_suffix"
[ -z "$plugindir" ] && plugindir="$prefix/$plugindir_suffix"
makefile_assign BINDIR "$bindir"
makefile_assign MANDIR "$mandir"
makefile_assign PLUGINDIR "$plugindir"
header_define PLUGIN_DIR "\"$plugindir\""

if [ "$enable_debug" = yes ]; then
	header_define DEBUG
	makefile_append CFLAGS -ggdb
fi

check_program "$CC"
check_program pkg-config

header_define _GNU_SOURCE
header_define _OPENBSD_SOURCE	# For OpenBSD extensions on NetBSD.
header_define _WITH_GETLINE	# For getline() on FreeBSD.

if check_function asprintf 'asprintf(NULL, "")' stdio.h; then
	header_define HAVE_ASPRINTF
else
	makefile_append SRCS compat/asprintf.c
fi

if ! check_function dlopen 'dlopen("", 0)' dlfcn.h; then
	if check_function "dlopen() in libdl" 'dlopen("", 0)' dlfcn.h "" \
	    -ldl; then
		makefile_append LDFLAGS_PROG -ldl
	else
		error "cannot determine how to use dlopen()"
	fi
fi

if check_function err 'err(0, "")' err.h; then
	header_define HAVE_ERR
else
	makefile_append SRCS compat/err.c
fi

if check_function getprogname "getprogname()" stdlib.h; then
	header_define HAVE_GETPROGNAME
fi

if ! check_function initscr "initscr()" curses.h "" -lcurses; then
	error "cannot determine how to use initscr()"
fi

if check_function pledge 'pledge("", "")' unistd.h; then
	header_define HAVE_PLEDGE
else
	makefile_append SRCS compat/pledge.c
fi

if check_function reallocarray "reallocarray(NULL, 0, 0)" stdlib.h; then
	header_define HAVE_REALLOCARRAY
else
	makefile_append SRCS compat/reallocarray.c
fi

if check_function resizeterm "resizeterm(0, 0)" curses.h "" -lcurses; then
	header_define HAVE_RESIZETERM
fi

if check_function strcasestr "strcasestr(NULL, NULL)" string.h; then
	header_define HAVE_STRCASESTR
else
	makefile_append SRCS compat/strcasestr.c
fi

if check_function strlcat "strlcat(NULL, NULL, 0)" string.h; then
	header_define HAVE_STRLCAT
else
	makefile_append SRCS compat/strlcat.c
fi

if check_function strlcpy "strlcpy(NULL, NULL, 0)" string.h; then
	header_define HAVE_STRLCPY
else
	makefile_append SRCS compat/strlcpy.c
fi

if check_function strsep "strsep(NULL, NULL)" string.h; then
	header_define HAVE_STRSEP
else
	makefile_append SRCS compat/strsep.c
fi

if check_function strtonum "strtonum(NULL, 0, 0, NULL)" stdlib.h; then
	header_define HAVE_STRTONUM
else
	makefile_append SRCS compat/strtonum.c
fi

if check_function swap16 "swap16(0)" endian.h; then
	header_define HAVE_OPENBSD_SWAP16
elif check_function "bswap16() in <machine/bswap.h>" "bswap16(0)" \
    "sys/types.h machine/bswap.h"; then
	header_define HAVE_NETBSD_BSWAP16
elif check_function "bswap16() in <sys/endian.h>" "bswap16(0)" \
    sys/endian.h; then
	header_define HAVE_FREEBSD_BSWAP16
elif check_function __builtin_bswap16 "__builtin_bswap16(0)"; then
	header_define HAVE___BUILTIN_BSWAP16
fi

if check_function use_default_colors "use_default_colors()" curses.h "" \
    -lcurses; then
	header_define HAVE_USE_DEFAULT_COLORS
fi

# Defining _GNU_SOURCE gives us the GNU version of strerror_r() which is
# incompatible with the POSIX version.
print_check "if strerror_r() is GNU"
if compile "" string.h "char *strerror_r(int, char *, size_t)"; then
	print_result yes
	header_define HAVE_GNU_STRERROR_R
	makefile_append SRCS compat/strerror_r.c
else
	print_result no
fi

print_check "for optreset"
if compile "" unistd.h "optreset = 0"; then
	print_result yes
	header_define HAVE_OPTRESET
else
	print_result no
	makefile_append SRCS compat/getopt.c
fi

if [ "$enable_aac" != no ] && check_header neaacdec.h && check_library faad &&
    check_header mp4v2/mp4v2.h && check_library mp4v2; then
	makefile_append IP aac
	makefile_assign LDFLAGS_aac "-lfaad -lmp4v2"
	enabled_ips="$enabled_ips aac"
fi

check_plugin_pkgconfig ffmpeg ip "libavcodec libavformat libavutil"

if check_plugin_pkgconfig flac ip flac; then
	# The include path provided by the flac pkg-config file has changed in
	# flac 1.3.0.
	if pkg-config --atleast-version 1.3.0 flac; then
		header_define HAVE_NEW_FLAC_INCLUDE_PATH
	fi
fi

if ! check_plugin_pkgconfig mad ip "mad id3tag" && [ "$enable_mad" != no ] &&
    check_header mad.h && check_library mad && check_header id3tag.h &&
    check_library id3tag; then
	makefile_append IP mad
	makefile_assign LDFLAGS_mad "-lmad -lid3tag"
	enabled_ips="$enabled_ips mad"
fi

check_plugin_pkgconfig mpg123 ip libmpg123

check_plugin_pkgconfig opus ip opusfile

if check_plugin_pkgconfig sndfile ip sndfile; then
	if pkg-config --atleast-version 1.0.23 sndfile; then
		header_define HAVE_SF_STR_GENRE
		header_define HAVE_SF_STR_TRACKNUMBER
	fi
fi

check_plugin_pkgconfig vorbis ip vorbisfile

check_plugin_pkgconfig wavpack ip wavpack

check_plugin_pkgconfig alsa op alsa

if check_plugin_pkgconfig ao op ao; then
	if pkg-config --atleast-version 1.0.0 ao; then
		header_define HAVE_AO_MATRIX
	fi
fi

if [ "$enable_oss" != no ]; then
	if ! check_header soundcard.h; then
		if check_header sys/soundcard.h; then
			header_define HAVE_SYS_SOUNDCARD_H
		else
			enable_oss=no
		fi
	fi

	if [ "$enable_oss" != no ]; then
		makefile_append OP oss
		if check_library ossaudio; then
			makefile_assign LDFLAGS_oss -lossaudio
		fi
		enabled_ops="$enabled_ops oss"
	fi
fi

check_plugin_pkgconfig portaudio op portaudio-2.0

check_plugin_pkgconfig pulse op libpulse-simple

if ! check_plugin_pkgconfig sndio op sndio && [ "$enable_sndio" != no ] &&
    check_header sndio.h && check_library sndio; then
	makefile_append OP sndio
	makefile_assign LDFLAGS_sndio -lsndio
	enabled_ops="$enabled_ops sndio"
fi

if [ "$enable_sun" != no -a "$(uname)" != OpenBSD ]; then
	# NetBSD has <sys/audioio.h>, Solaris has <sys/audio.h>.
	if ! check_header sys/audioio.h sys/types.h; then
		if check_header sys/audio.h; then
			header_define HAVE_SYS_AUDIO_H
		else
			enable_sun=no
		fi
	fi

	if [ "$enable_sun" != no ]; then
		makefile_append OP sun
		enabled_ops="$enabled_ops sun"
	fi
fi

echo
echo "bindir:          $bindir"
echo "mandir:          $mandir"
echo "plugindir:       $plugindir"
echo "debug build:     $enable_debug"
echo "input plug-ins:  ${enabled_ips# }"
echo "output plug-ins: ${enabled_ops# }"
