#!/bin/sh
# SPDX-FileCopyrightText: 2019-2023 Badwolf Authors <https://hacktivis.me/projects/badwolf>
# SPDX-License-Identifier: BSD-3-Clause

VERSION=1.4.0
VERSION_FULL=${VERSION}$(./version.sh)
DEPS="gtk+-3.0 libxml-2.0"

SRCS="bookmarks.c userscripts.c fmt.c fmt_test.c uri.c uri_test.c keybindings.c downloads.c badwolf.c man_uri_scheme.c"
OBJS="bookmarks.o userscripts.o fmt.o uri.o keybindings.o downloads.o badwolf.o man_uri_scheme.o"
OBJS_test="fmt_test.o uri_test.o bookmarks_test.o"
EXE=badwolf
EXE_test="fmt_test uri_test bookmarks_test"
DOCS="usr.bin.badwolf README.md KnowledgeBase.md interface.md"

TRANS="fr pt_BR sr tr de vi"
TRANS_MAN="de fr sr tr vi"

lint_targets=""

min_webkitgtk=2.32.0
min_glib_guri=2.66.0

arg0="$0"
args="$@"

usage() {
cat <<END
Usage: [variables] configure [variables]

Variables:
  PREFIX=DIR
  BINDIR=DIR
  MANDIR=DIR
  DOCDIR=DIR
  DATADIR=DIR
  APPSDIR=DIR

  PKGCONFIG=BIN
  MSGFMT=BIN
  INKSCAPE=BIN
  CC=BIN
  CFLAGS=OPTIONS
  ED=BIN
  LDFLAGS=OPTIONS
  EXTRA_CFLAGS=OPTIONS
  XGETTEXT=BIN
  MSGMERGE=BIN
  MANDOC=BIN
  SHELLCHECK=BIN
  FLAWFINDER=BIN
  REUSE=BIN

  WITH_WEBKITGTK=(4.0|4.1)
  WITH_URI_PARSER=(guri|libsoup2)

Variables are set in the following order: Default, Environment, Arguments

Dependencies: See README.md
END
}

is_ok() {
	status="$?"

	if test $status -eq 0; then
		printf " OK\n"
	else
		printf " FAIL\n"
	fi

	return $status
}

required() {
	is_ok || exit 1
}

pkg_config_check() {
	printf 'Checking: %s %s ...' "${PKGCONFIG}" "$*"
	"${PKGCONFIG}" "$@"
	is_ok
}

## User configuration

# defaults
PREFIX="${PREFIX:-/usr/local}"
PKGCONFIG="${PKGCONFIG:-pkg-config}"
MSGFMT="${MSGFMT:-msgfmt}"
INKSCAPE="${INKSCAPE:-inkscape}"
CC="${CC:-cc}"
CFLAGS="${CFLAGS:--g -O2 -D_FORTIFY_SOURCE=2}"
ED="${ED:-ed}"
XGETTEXT="${XGETTEXT:-xgettext}"
MSGMERGE="${MSGMERGE:-msgmerge}"
MANDOC="${MANDOC:-mandoc}"
SHELLCHECK="${SHELLCHECK:-shellcheck}"
FLAWFINDER="${FLAWFINDER:-flawfinder}"
REUSE="${REUSE:-reuse}"


# Also allow variables through arguments
for i; do
	case "$i" in
	-h|--help)
		usage
		exit 1
	;;
	-*)
		printf "Unknown argument ‘%s’\n" "${i}"
		usage
		exit 1
	;;
	*=*)
		# shellcheck disable=SC2163
		export "$i"
		shift
	;;
	*)
		printf "Unknown argument ‘%s’\n" "${i}"
		usage
		exit 1
	;;
	esac
done

# Fallback definitions for dirs, based on $PREFIX
BINDIR="${BINDIR:-${PREFIX}/bin}"
MANDIR="${MANDIR:-${PREFIX}/share/man}"
DOCDIR="${DOCDIR:-${PREFIX}/share/doc/badwolf-${VERSION}}"
DATADIR="${DATADIR:-${PREFIX}/share/badwolf}"
APPSDIR="${APPSDIR:-${PREFIX}/share/applications}"

# Add some extra CFLAGS
CFLAGS="${CFLAGS} -Wall -Wextra -Wconversion -Wsign-conversion -Werror=implicit-function-declaration -Werror=implicit-int -Werror=vla ${EXTRA_CFLAGS}"

## System checks
# commands
printf 'Checking %s command existence ...' "${PKGCONFIG}"
command -v "${PKGCONFIG}" >/dev/null ; required

printf 'Checking %s command existence ...' "${CC}"
command -v "${CC}" >/dev/null ; required

printf 'Checking %s command existence ...' "${ED}"
if command -v "${ED}" >/dev/null ; is_ok
then
	:
else
	echo 'Warning: Updating of *.pot translation files disabled, you may want to install ed(1)'
	ED="false"
fi

printf 'Checking %s command existence ...' "${MANDOC}"
if command -v "${MANDOC}" >/dev/null ; is_ok
then
	lint_targets="${lint_targets} lint_mandoc"
else
	echo 'Warning: manpage linting via mandoc(1) disabled'
	MANDOC="true"
fi

printf 'Checking %s command existence ...' "${XGETTEXT}"
if command -v "${XGETTEXT}" >/dev/null ; is_ok
then
	:
else
	echo 'Warning: translation updates disabled'
	XGETTEXT="true"
fi

printf 'Checking %s command existence ...' "${MSGMERGE}"
if command -v "${MSGMERGE}" >/dev/null ; is_ok
then
	:
else
	echo 'Warning: translation updates disabled'
	MSGMERGE="true"
fi

printf 'Checking %s command existence ...' "${SHELLCHECK}"
if command -v "${SHELLCHECK}" >/dev/null ; is_ok
then
	lint_targets="${lint_targets} lint_shellcheck"
else
	echo 'Warning: shell linting via shellcheck(1) disabled'
	SHELLCHECK="true"
fi

printf 'Checking %s command existence ...' "${FLAWFINDER}"
if command -v "${FLAWFINDER}" >/dev/null ; is_ok
then
	lint_targets="${lint_targets} lint_flawfinder"
else
	echo 'Warning: C analysis via flawfinder(1) disabled'
	FLAWFINDER="true"
fi

printf 'Checking %s command existence ...' "${REUSE}"
if command -v "${REUSE}" >/dev/null ; is_ok
then
	lint_targets="${lint_targets} lint_reuse"
else
	echo 'Warning: License linting via reuse(1) disabled'
	REUSE="true"
fi

echo

# pkg-config
for dep in ${DEPS}
do
	pkg_config_check --exists "$dep" || exit 1
done

case "${WITH_WEBKITGTK}n" in
	4.1n)
		pkg_config_check --atleast-version="${min_webkitgtk}" webkit2gtk-4.1 || exit 1
		DEPS="${DEPS} webkit2gtk-4.1"
	;;
	4.0n)
		pkg_config_check --atleast-version="${min_webkitgtk}" webkit2gtk-4.0 || exit 1
		DEPS="${DEPS} webkit2gtk-4.0"
	;;
	n)
		echo "notice: Packagers should consider setting the ABI version (4.0 or 4.1) in WITH_WEBKITGTK" >&2
		if pkg_config_check --atleast-version="${min_webkitgtk}" webkit2gtk-4.1
		then
			DEPS="${DEPS} webkit2gtk-4.1"
		else
			pkg_config_check --atleast-version="${min_webkitgtk}" webkit2gtk-4.0 || exit 1
			DEPS="${DEPS} webkit2gtk-4.0"
		fi
	;;
	*)
		echo "error: invalid webkit2gtk version in WITH_WEBKITGTK environment variable, must be 4.0 or 4.1" >&2
		exit 1
	;;
esac

case "${WITH_URI_PARSER}n" in
	gurin)
		echo "URI parser selected: GUri from glib-2.0"
		pkg_config_check --atleast-version="${min_glib_guri}" glib-2.0 || exit 1
		DEPS="${DEPS} glib-2.0"
	;;
	libsoup2n)
		echo "URI parser selected: libsoup-2.4"
		pkg_config_check libsoup-2.4 || exit 1
		DEPS="${DEPS} libsoup-2.4"
		CFLAGS="${CFLAGS} -DUSE_LIBSOUP2"

		if echo "${DEPS}" | grep -q 'webkit2gtk-4.1'
		then
			echo 'warning: libsoup2 selected while WebKitGTK with libsoup3 API is used' >&2
		fi
	;;
	n)
		echo "notice: Packagers should consider setting the URI parsing library (guri or libsoup2) in WITH_URI_PARSER" >&2

		if echo "${DEPS}" | grep -q 'webkit2gtk-4.0'
		then
			pkg_config_check libsoup-2.4 || exit 1
			echo "URI parser selected: libsoup-2.4"
			DEPS="${DEPS} libsoup-2.4"
			CFLAGS="${CFLAGS} -DUSE_LIBSOUP2"
		else
			pkg_config_check --atleast-version="${min_glib_guri}" glib-2.0 || exit 1
			echo "URI parser selected: GUri from glib-2.0"
			DEPS="${DEPS} glib-2.0"
		fi
	;;
	*)
		echo "error: invalid uri provider in WITH_URI_PARSER environment variable, must be guri or libsoup2" >&2
	;;
esac

printf 'Using pkg-config to get CFLAGS for %s ...' "${DEPS}"
get_cflags() { "${PKGCONFIG}" --cflags "${DEPS}"; }
DEPS_cflags="$(get_cflags)"
required

printf 'Using pkg-config to get LIBS for %s ...' "${DEPS}"
get_libs() { "${PKGCONFIG}" --libs "${DEPS}"; }
DEPS_libs="$(get_libs)"
required

echo

printf 'Writing to configure.h ...'
cat >configure.h <<EOF
#define DATADIR "${DATADIR}"
#define PACKAGE "Badwolf"
#define _XOPEN_SOURCE 700
#define _POSIX_C_SOURCE 200809L
#define VERSION "${VERSION_FULL}"
EOF
is_ok

## Configuration write
printf 'Writing to config.ninja ...'
cat >config.ninja <<EOF
# Autogenerated by $arg0 $args
rule gen_config
  command = $arg0 "$args"
  generator = 1

build config.ninja: gen_config configure

PREFIX = ${PREFIX}
PKGCONFIG = ${PKGCONFIG}
MSGFMT = ${MSGFMT}
INKSCAPE = ${INKSCAPE}
CC = ${CC}
CFLAGS = ${CFLAGS}
LDFLAGS = ${LDFLAGS}
ED = ${ED}
MANDOC = ${MANDOC}
XGETTEXT = ${XGETTEXT}
MSGMERGE = ${MSGMERGE}
SHELLCHECK = ${SHELLCHECK}
FLAWFINDER = ${FLAWFINDER}
REUSE = ${REUSE}

DEPS_cflags = ${DEPS_cflags}
DEPS_libs = ${DEPS_libs}

GETTEXT_OPTS = --copyright-holder="Badwolf Authors <https://hacktivis.me/projects/badwolf>" --package-name="Badwolf" --package-version="${VERSION_FULL}" --msgid-bugs-address="contact+badwolf-msgid@hacktivis.me"

rule xgettext
  command = \$XGETTEXT --keyword=_ --language=C --from-code=UTF-8 -o \$out --add-comments --sort-output --foreign-user --no-location --no-wrap \$GETTEXT_OPTS \$in && \$ED -s \$out <po/pot_license.ed

rule msgmerge
  # touch: msgmerge doesn't always updates timestamps
  command = \$MSGMERGE --update --backup=off \$out \$in && touch \$out

rule xgettext_man
  command = if test -e \$out; $
then po4a-updatepo --format man -M utf-8 --master \$in \$GETTEXT_OPTS --po \$out && \$ED -s \$out <po/pot_license.ed;$
else po4a-gettextize --format man -M utf-8 --master \$in \$GETTEXT_OPTS --po \$out && \$ED -s \$out <po/pot_license.ed;$
fi

rule cc_exe
  command = \$CC -std=c11 \$CFLAGS -include configure.h \$DEPS_cflags -o \$out \$in \$LDFLAGS \$DEPS_libs

rule cc_obj
  command = \$CC -std=c11 \$CFLAGS -include configure.h \$DEPS_cflags -c -o \$out \$in

build badwolf: cc_exe ${OBJS}

EOF
all="badwolf"

for obj in ${OBJS}; do
	obj_c="$(echo "$obj" | sed -e 's;\.o;.c;g')"
	echo -n "build ${obj}: cc_obj ${obj_c} | "
	grep '#include "' "${obj_c}" | sed -e 's;#include ";;' -e 's;";;' | tr '\n' ' '
	echo
done >>config.ninja

if [ "$ED" != "false" ]; then
	echo "build po/messages.pot: xgettext ${SRCS} | po/pot_license.ed"
	echo 'build po/manpage.pot: xgettext_man badwolf.1 | po/pot_license.ed'
fi >>config.ninja

for trans in ${TRANS}; do
	echo "build po/${trans}.po: msgmerge po/messages.pot"
	echo "build locale/${trans}/LC_MESSAGES/Badwolf.mo: po2mo po/${trans}.po"
	all="${all} locale/${trans}/LC_MESSAGES/Badwolf.mo"
done >>config.ninja

for man in ${TRANS_MAN}; do
	echo "build po/${man}_man.po: xgettext_man badwolf.1"
	echo "build badwolf.${man}.1: translate_manpage po/${man}_man.po"
	bundled="${bundled} badwolf.${man}.1"
	trans_man="${trans_man} badwolf.${man}.1"
done >>config.ninja

for i in 24 32 48 64 128 256; do
	echo "build icons/hicolor/${i}x${i}/apps/badwolf.png: gen_icon icons/hicolor/scalable/apps/badwolf.svg
  width = $i
  height = $i"
	bundled="${bundled} icons/hicolor/${i}x${i}"
	icons_list="${icons_list} icons/hicolor/${i}x${i}/apps/badwolf.png"
done >>config.ninja

cat >>config.ninja <<EOF
build icons: phony | ${icons_list}
build trans_man: phony | ${trans_man}
build bundled: phony | icons trans_man

default ${all}

rule remove
  command = rm -fr \${in} \${foo}

build install: install | ${all}
build clean: remove
  foo = ${all} ${OBJS} ${OBJS_test}
build distclean: remove | clean
  foo = config.ninja configure.h install.sh
build fullclean: remove | distclean
  foo = ${bundled}
EOF

is_ok

printf 'Writing to ./install.sh ...'
cat >install.sh <<EOF
# Autogenerated by $arg0 $args

# doins <DIR> <filename ...>
doins() {
	dir="\${DESTDIR}/\$1"; shift
	mkdir -p "\${dir}/" || exit 1

	echo "\$@ → \${dir}/"
	cp -pr "\$@" "\${dir}/"
}
# newins <DIR> <orig-filename> <dest-filename>
newins() {
	dir="\${DESTDIR}/\$1"
	mkdir -p "\${dir}/" || exit 1

	echo "\$2 → \${dir}/\$3"
	cp -pr "\$2" "\${dir}/\$3"
}

doins "${BINDIR}" ./badwolf
doins "${MANDIR}/man1" ./badwolf.1
for man in ${TRANS_MAN}; do
	newins "${MANDIR}/\${man}/man1" "./badwolf.\${man}.1" "badwolf.1"
done
doins "${DATADIR}" ./locale
doins "${DATADIR}" ./interface.css
doins "${APPSDIR}" badwolf.desktop
doins "${DOCDIR}" ${DOCS}
doins "${PREFIX}/share" icons

printf "\nNote: An example AppArmor profile has been installed at '${DOCDIR}/usr.bin.badwolf'\n"
EOF

is_ok

echo 'Done, you can now run ninja or samu'
