#!/bin/sh

PREFIX='/usr/local'
BINDIR='${PREFIX}/bin'
MANDIR='${PREFIX}/man'
CONFIG_SHELL='sh'
CONFIG_MAKEFILE='Mkfile'

BMK_VERSION="0.1"
BMK_EMAIL="benni@stuerz.xyz"

build_alias=
host_alias=
cross_compiling=no
warnings=yes

for arg
do
	value=`echo "x$arg" | sed 's/^x[^=]*=//'`
	case "$arg" in
	--prefix=*)
		PREFIX=$value
		;;
	--bindir=*)
		BINDIR=$value
		;;
	--mandir=*)
		MANDIR=$value
		;;
	--with-shell=*)
		CONFIG_SHELL=$value
		;;
	--with-makefile=*)
		CONFIG_MAKEFILE=$value
		;;
	--build=*)
		build_alias=$value
		;;
	--host=*)
		host_alias=$value
		;;
	--target=*)
		echo "warn: '$arg' ignored, because this project is not a cross-compiler" >&2
		;;
	-h|--help)
		cat <<EOF
Usage: ./configure [OPTION]...

This script configures bmk v${BMK_VERSION}.

Configuration:
  --build=MACHINE       specify build machine
  --host=MACHINE        specify host machine
  --prefix=DIR          top-level installation directory [$PREFIX]
  --bindir=DIR          executables [$BINDIR]
  --mandir=DIR          man documentation [$MANDIR]
  --with-shell=SHELL    default value for the SHELL variable [$CONFIG_SHELL]
  --with-makefile=SHELL default name of the makefile [$CONFIG_MAKEFILE]
  --disable-warnings    disable compiler warnings

Environment variables:
  CC                    C compiler command
  CPP                   C preprocessor command
  CFLAGS                C compiler flags
  CPPFLAGS              C preprocessor flags
  LDFLAGS               Linker flags

Note: This configure script is no longer generated by autoconf.
Report bugs to <${BMK_EMAIL}>.
EOF
		exit 1
		;;
	--enable-warnings)
		warnings=yes
		;;
	--disable-warnings)
		warnings=no
		;;
	-*)
		echo "warn: unrecognized option: $arg" >&2
		;;
	esac
done

try_preprocess='
{
	echo "conftest.c:";
	cat conftest.c;
	echo "$CPP -o conftest$$.i conftest.c $CPPFLAGS $CFLAGS";
	$CPP -o conftest$$.i conftest.c $CPPFLAGS $CFLAGS;
} >>config.log 2>&1'
try_compile='
{
	echo "conftest.c:";
	cat conftest.c;
	echo "$CC -c -o conftest$$.o conftest.c $CPPFLAGS $CFLAGS";
	$CC -c -o conftest$$.o conftest.c $CPPFLAGS $CFLAGS;
} >>config.log 2>&1'
try_link='
{
	echo "conftest.c:";
	cat conftest.c;
	echo "$CC -o conftest$$ conftest.c $CPPFLAGS $CFLAGS $LDFLAGS";
	$CC -o conftest$$ conftest.c $CPPFLAGS $CFLAGS $LDFLAGS;
} >>config.log 2>&1'
translate='tr a-z A-Z | sed "s/[^A-Z]/_/g"'

clean="rm -f Makefile.new conftest$$ conftest$$.i conftest$$.o conftest.c"
trap "$clean; exit 1" 1 2 13 15
fail="$clean; exit 1"

rm -f config.h config.mk config.log

if test "$host_alias"; then
	if test -z "$build_alias"; then
		cross_compiling=maybe
	elif test "$host_alias" != "$build_alias"; then
		cross_compiling=yes
	fi
fi

if test -z "$CC"; then
	cc_prefix=
	if test "$host_alias"; then
		cc_prefix="${host_alias}-"
	fi
	for cc in cc gcc egcc clang pcc tcc
	do
		CC="${cc_prefix}${cc}"
		echo "checking for $cc..." >>config.log
		echo -n "checking for $cc... "
		cat >conftest.c <<EOF
int main ()
{
	return 0;
}
EOF
		if eval "$try_link"; then
			break
		fi
		CC=
		echo no
	done
	if test -z "$CC"; then
		echo no
		echo "error: no C compiler found." >>config.log
		echo "error: no C compiler found." >&2
		$fail
	fi
	echo "$CC"
fi

echo 'checking if the C compiler works...' >>config.log
echo -n 'checking if the C compiler works... '
cat >conftest.c <<EOF
int main ()
{
	return 42;
}
EOF

eval "$try_link"
if test $? -ne 0; then
	echo no
	echo 'error: compiler cannot produce working binary' >&2
	$fail
fi

if test "$cross_compiling" != 'yes'; then
	./conftest$$ >>config.log 2>&1
	if test $? -eq 42; then
		cross_compiling=no
	elif test "$cross_compiling" = 'maybe'; then
		cross_compiling=yes
	else
		echo no
		echo 'error: binary produced by compiler doesnt work' >&2
		$fail
	fi
fi
echo yes

echo "checking if we are cross compiling... $cross_compiling" >>config.log
echo "checking if we are cross compiling... $cross_compiling"

echo 'checking how to run the C preprocessor...' >>config.log
echo -n 'checking how to run the C preprocessor... '
if test -z "$CPP"; then
	for cpp in "$CC -E" "$CC -E -traditional-cpp" 'cpp' '/lib/cpp'
	do
		CPP=$cpp
		eval "$try_preprocess" &&break
		CPP=
	done
	if test -z "$CPP"; then
		echo no
		echo 'error: dont know how to run the C preprocessor' >&2
		$fail
	fi
fi
echo "$CPP"

echo 'checking C compiler vendor...' >>config.log
echo -n 'checking C compiler vendor... '
for v in 'pcc:__PCC__' 'clang:__clang__' 'gcc:__GNUC__'; do
	vendor=`echo "$v" | sed 's/:.*$//'`
	macro=`echo "$v" | sed 's/^[^:]*://'`
	cat >conftest.c <<EOF
#if !($macro)
this is an error
#endif
EOF
	if eval "$try_compile"; then
		cc_vendor=$vendor
		break
	fi
done
if test -z "$cc_vendor"; then
	cc_vendor=unknown
fi
echo "$cc_vendor"

cflags_gcc='
-Wno-return-type
-Wno-missing-parameter-type
'
cflags_clang='
-Wno-deprecated-non-prototype
'
cflags_pcc='-Wno-missing-prototypes'
cflags_unknown=''
cflags='
-ansi
-O2
'

if test "$warnings" = 'yes'; then
	cflags="$cflags -Wall -Wextra"
	eval cflags="\"\$cflags \$cflags_$cc_vendor\""
else
	cflags="$cflags -w"
fi

cat >conftest.c <<EOF
int main ()
{
	return 0;
}
EOF
for flag in $cflags
do
	echo "checking whether the C compiler accepts $flag..." >>config.log
	echo -n "checking whether the C compiler accepts $flag... "
	saved_CFLAGS=$CFLAGS
	CFLAGS=$flag
	if eval "$try_link"; then
		echo yes
		CFLAGS="$saved_CFLAGS $flag"
	else
		echo no
		CFLAGS=$saved_CFLAGS
	fi
done

cat >config.h <<EOF
/* Configuration */
#define SHELL "$CONFIG_SHELL"
#define MAKEFILE "$CONFIG_MAKEFILE"

/* System Extensions */

#define _ALL_SOURCE 1		/* Enable extensions on AIX, Interix, z/OS */
#define _DARWIN_C_SOURCE 1	/* Enable general extensions on MacOS */
#define __EXTENSIONS__ 1	/* Enable general extensions on Solaris */
#define _GNU_SOURCE 1		/* Enable GNU extensions on systems that have them */
#define _BSD_SOURCE 1		/* Enable BSD functions */
#define _NETBSD_SOURCE 1	/* Enable NetBSD compatibility extensions on Minix */
#define _OPENBSD_SOURCE 1	/* Enable OpenBSD compatibility extensions on NetBSD */

/* Headers */
EOF
headers='
err.h
fnmatch.h
inttypes.h
libgen.h
limits.h
memory.h
stdbool.h
stdint.h
stdio.h
stdlib.h
string.h
strings.h
sys/stat.h
sys/time.h
sys/timeb.h
sys/types.h
sys/wait.h
time.h
unistd.h
'

for header in $headers
do
	echo "checking for <$header>..." >>config.log
	echo -n "checking for <$header>... "

	cat >conftest.c <<EOF
#include <$header>
int main ()
{
	return 0;
}
EOF

	name=`echo "$header" | eval "$translate"`
	if eval "$try_preprocess"; then
		echo "#define HAVE_$name 1" >>config.h
		echo yes
	else
		echo "#undef HAVE_$name" >>config.h
		echo no
	fi
done

echo >>config.h
echo "/* Functions */" >>config.h
funcs='
basename
clock_gettime
dirname
fmemopen
fnmatch
fork
ftime
getcwd
gettimeofday
lstat
memmove
reallocarray
realpath
strdup
strerror
strsep
strtol
'

# tcc needs -static, otherwise linking will falsly succeed
old_LDFLAGS=$LDFLAGS
#LDFLAGS="$LDFLAGS -static"
for func in $funcs
do
	echo "checking for $func()..." >>config.log
	echo -n "checking for $func()... "

	cat >conftest.c <<EOF
char $func ();
char (*f) ();

int main ()
{
	f = $func;
	return 0;
}
EOF

	name=`echo "$func" | eval "$translate"`
	if eval "$try_link"; then
		echo yes
		echo "#define HAVE_$name 1" >>config.h
	else
		echo no
		echo "#undef HAVE_$name" >>config.h
	fi
done
LDFLAGS=$old_LDFLAGS

echo >>config.h
echo '/* Types */' >>config.h
for type in 'size_t=unsigned int' 'ssize_t=int' 'pid_t=int'
do
	name=`echo "$type" | sed 's/=.*//'`
	value=`echo "$type" | sed 's/^[^=]*=//'`

	echo "checking for $name..." >>config.log
	echo -n "checking for $name... "

	cat >conftest.c <<EOF
#include "config.h"
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_STDIO_H
#include <stdio.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif

extern $name x;

int main ()
{
	return 0;
}
EOF

	if eval "$try_compile"; then
		echo yes
	else
		echo no
		echo "#define $name $value" >>config.h
	fi
done

echo 'checking if union wait exists...' >>config.log
echo -n 'checking if union wait exists... '
cat >conftest.c <<EOF
#include "config.h"
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif

int main ()
{
	union wait w;
	wait (&w);
	return 0;
}
EOF
if eval "$try_compile"; then
	echo yes
	echo '#define HAVE_UNION_WAIT 1' >>config.h
else
	echo no
	echo '#undef HAVE_UNION_WAIT' >>config.h
fi

echo 'checking if struct timespec exists...' >>config.log
echo -n 'checking if struct timespec exists... '
cat >conftest.c <<EOF
#include "config.h"
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

int main ()
{
	struct timespec ts;
	ts.tv_sec = 0;
	ts.tv_nsec = 0;
	return 0;
}
EOF
if eval "$try_compile"; then
	echo yes
	echo '#define HAVE_TIMESPEC 1' >>config.h
else
	echo no
	echo '#undef HAVE_TIMESPEC' >>config.h
fi

echo 'checking if struct stat has st_mtim...' >>config.log
echo -n 'checking if struct stat has st_mtim... '
cat >conftest.c <<EOF
#include "config.h"
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

int main ()
{
	struct stat st;
	st.st_mtim.tv_sec = 0;
	return 0;
}
EOF
if eval "$try_compile"; then
	echo yes
	echo '#define HAVE_STAT_MTIM 1' >>config.h
else
	echo no
	echo '#undef HAVE_STAT_MTIM ' >>config.h
fi

echo >>config.h
echo '/* Special Tests */' >>config.h

echo 'checking if <sys/file.h> defines F_OK...' >>config.log
echo -n 'checking if <sys/file.h> defines F_OK... '
cat >conftest.c <<EOF
/* 2.11BSD defines *_OK macros in <sys/file.h> */
#ifdef F_OK
#undef F_OK
#endif
#include <sys/file.h>

int main ()
{
	return F_OK;
}
EOF
if eval "$try_compile"; then
	echo yes
	echo "#define NEED_SYS_FILE_H 1" >>config.h
else
	echo no
	echo "#undef NEED_SYS_FILE_H" >>config.h
fi

echo 'checking if <time.h> is needed...' >>config.log
echo -n 'checking if <time.h> is needed... '
cat >conftest.c <<EOF
/* BSDs include <time.h> in <sys/time.h> */
/* When including both in 2.11BSD, struct tm is defined twice. */
#define tm test_tm
#include <sys/time.h>
#undef tm

struct test_tm x;
EOF
if eval "$try_compile"; then
	echo no
	echo "#undef NEED_TIME_H" >>config.h
else
	echo yes
	echo "#define NEED_TIME_H 1" >>config.h
fi

echo 'checking if the C compiler supports C99 designated intializers...' >>config.log
echo -n 'checking if the C compiler supports C99 designated intializers... '
cat >conftest.c <<EOF
/* Some compilers are just too old */
struct test {
	char *name;
	int value;
} obj = { .name = "test", .value = 42 };
EOF
if eval "$try_compile"; then
	echo yes
	echo '#define HAVE_DESIGNATED_INITIALIZERS 1' >>config.h
else
	echo no
	echo '#undef HAVE_DESIGNATED_INITIALIZERS' >>config.h
fi

echo 'checking if strsep() actually works...' >>config.log
echo -n 'checking if strsep() actually works... '
cat >conftest.c <<EOF
/* 2.11BSD has a different strsep() functions, which is more like strtok(). */
#include <stdio.h> /* NULL */

extern char *strsep ();
extern int strcmp ();

int main ()
{
	char *s, *t, x[] = "/usr/local/bin", *words[] = {
		"",
		"usr",
		"local",
		"bin"
	};
	int i;

	for (i = 0, s = x; i < 4 && (t = strsep (&s, "/")) != NULL; ++i) {
		if (strcmp (t, words[i]) != 0)
			return i;
	}
	return 0;
}
EOF
if eval "$try_link" && { ./conftest$$ >>config.log 2>&1 || test "$cross_compiling" = 'yes'; }; then
	echo yes
	echo '#define WORKS_STRSEP 1' >>config.h
else
	echo no
	echo '#undef WORKS_STRSEP' >>config.h
fi

echo 'checking if sys_errlist[] exists...' >>config.log
echo -n 'checking if sys_errlist[] exists... '
cat >conftest.c <<EOF
#include <stdio.h>
extern char *sys_errlist[];
int main ()
{
	printf ("%s\n", sys_errlist[1]);
	return 0;
}
EOF
if eval "$try_link"; then
	echo yes
	echo '#define HAVE_SYS_ERRLIST 1' >>config.h
else
	echo no
	echo '#undef HAVE_SYS_ERRLIST' >>config.h
fi

echo 'checking if the C compiler understands void *...' >>config.log
echo -n 'checking if the C compiler understands void *... '
cat >conftest.c <<EOF
#include <stdio.h>

void *
getptr (ptr)
void *ptr;
{
	return ptr;
}

int main ()
{
	int x = 42;
	void *p = &x;
	p = getptr (p);
	return *(int *)p;
}
EOF
if eval "$try_compile"; then
	echo yes
	echo '#define HAVE_VOID_PTR 1' >>config.h
else
	echo no
	echo '#undef HAVE_VOID_PTR' >>config.h
fi

echo 'checking if the C compiler understands void functions...' >>config.log
echo -n 'checking if the C compiler understands void functions... '
cat >conftest.c <<EOF
#include <stdio.h>

void print_int (x)
int x;
{
	printf ("%d\n", x);
}

int main ()
{
	print_int (42);
	return 0;
}
EOF
if eval "$try_compile"; then
	echo yes
	echo '#define HAVE_VOID_FUNC 1' >>config.h
else
	echo no
	echo '#undef HAVE_VOID_FUNC' >>config.h
fi

echo 'checking if the C compiler understands _Bool...' >>config.log
echo -n 'checking if the C compiler understands _Bool... '
cat >conftest.c <<EOF
_Bool f ()
{
	return 1;
}

int main ()
{
	return f () ? 0 : 1;
}
EOF
if eval "$try_compile"; then
	echo yes
	echo '#define HAVE__BOOL 1' >>config.h
else
	echo no
	echo '#undef HAVE__BOOL' >>config.h
fi

echo 'checking if the C compiler understands const...' >>config.log
echo -n 'checking if the C compiler understands const... '
cat >conftest.c <<EOF
int main ()
{
	int x;
	const int *p = &x;
	x = 0;
	return *p;
}
EOF
if eval "$try_compile"; then
	echo yes
else
	echo no
	echo '#define const' >>config.h
fi

cat >config.mk <<EOF
CC = $CC
CPP = $CPP
CFLAGS = $CFLAGS
CPPFLAGS = $CPPFLAGS -DHAVE_CONFIG_H -DBMK_VERSION=\"${BMK_VERSION}\" -DBMK_EMAIL=\"${BMK_EMAIL}\"
LDFLAGS = $LDFLAGS

PREFIX = $PREFIX
BINDIR = $BINDIR
MANDIR = $MANDIR
EOF

sed	-e 's/^-include.*/# &/'	\
	-e 's/^[a-zA-Z]* ?=.*$/# &/'	\
	< Mkfile > Makefile.new

cat config.mk Makefile.new >Makefile
echo "$CFLAGS" >compile_flags.txt

$clean
