#!/hint/bash

# conf.sh - implicit libretools configuration
#
# This may be included with or without `set -euE`.
# In order to be used inside librechroots, this script assumes nothing
# of the environment, other than the optional $SUDO_USER (invalid in-chroot).
# Its main purpose it to canonicalize the in-chroot environment and paths,
# so that libretools needs not be installed in every librechroot,
# unlike the host environment, where libretools can re-configure itself.
# However, some functions expect `librelib messages` to be pre-sourced,
# namely, the getters and setters (load_conf(), set_var(), set_var()),
# which are useful and meaningful only for the host environment.
# This is also where $LIBREUSER is defined, which most other scripts depend on.
# $LIBREUSER is $SUDO_USER unless UID->0, or $USER otherwise.
#
# Copyright (C) 2012-2015, 2017-2018, 2024  Luke T. Shumaker <lukeshu@parabola.nu>
# Copyright (C) 2020, 2023  Bill Auger <bill-auger@programmer.net>
# Copyright (C) 2024  Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
#
# SPDX-License-Identifier: GPL-2.0-or-later
#
# This file is part of Parabola Libretools.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# include guard
[[ ! -v _INCLUDE_CONF_SH ]] && readonly _INCLUDE_CONF_SH='' || return 0

## prepare environment ##

# define $LIBREUSER
if [[ "$(id -u "${SUDO_USER:-root}" 2>/dev/null)" == 0 ]]; then
	unset SUDO_USER
fi
LIBREUSER="${SUDO_USER:-$USER}"

# define $LIBREHOME
# $LIBREHOME is the default parent directory of $WORKDIR (per /etc/libretools.conf)
# default: ~$LIBREUSER/ unless SUDO_USER != $USER, $HOME/$LIBREUSER/ otherwise
# NOTE: $WORKDIR and $LIBREHOME/.cache must be writable by $LIBREUSER
if [[ $LIBREUSER == "$USER" ]]; then
	LIBREHOME=$HOME
else
	eval "LIBREHOME=~$LIBREUSER"
fi

# define $XDG_CONFIG_HOME and $XDG_CACHE_HOME, if not set already
if [[ -z ${XDG_CONFIG_HOME:-} ]]; then
	export XDG_CONFIG_HOME="${LIBREHOME}/.config"
fi
if [[ -z ${XDG_CACHE_HOME:-} ]]; then
	export XDG_CACHE_HOME="${LIBREHOME}/.cache"
fi

## Low-level generic functions ##

# Usage: list_files $slug
# Lists the configuration files to be considered for $slug.
# Later files should take precedence over earlier files.
list_files() {
	local slug=$1
	local sysconfdir=${_librelib_conf_sh_sysconfdir:-/etc}
	local pkgconfdir=${_librelib_conf_sh_pkgconfdir:-/etc/libretools.d}
	case $slug in
		abs)
			echo "${sysconfdir}/$slug.conf"
			echo "$LIBREHOME/.$slug.conf"
			;;
		makepkg)
			local manual="${MAKEPKG_CONF:-}"
			local system="${sysconfdir}/$slug.conf"
			local olduser="$LIBREHOME/.$slug.conf"
			local newuser="$XDG_CONFIG_HOME/pacman/$slug.conf"
			if [[ $manual != "$system" && -r $manual ]]; then
				# Manually-specified file
				echo "$manual"
			else
				# Normal file lookup
				echo "$system"
				if [[ -r $olduser && ! -r $newuser ]]; then
					echo "$olduser"
				else
					echo "$newuser"
				fi
			fi
			;;
		libretools)
			echo "${sysconfdir}/$slug.conf"
			echo "$XDG_CONFIG_HOME/libretools/$slug.conf"
			;;
		*)
			echo "${pkgconfdir}/$slug.conf"
			echo "$XDG_CONFIG_HOME/libretools/$slug.conf"
			;;
	esac
}

# Usage: list_envvars $slug
# Lists which env-vars shall over-ride the config files for $slug.
list_envvars() {
	local slug=$1
	case $slug in
		makepkg)
			printf '%s\n' \
				PKGDEST SRCDEST SRCPKGDEST LOGDEST \
				BUILDDIR \
				PKGEXT SRCEXT \
				GPGKEY PACKAGER \
				CARCH
			;;
		libretools)
			printf '%s\n' DIFFPROG
			;;
		*) : ;;
	esac
}

## High-level generic functions ##

# Usage: load_conf {$slug.conf|$abspath} [VAR1 VAR2...]
#
# Loads the configuration files for $slug in the proper order;
# and optionally, verify that specified variables are set.
load_conf() {
	[[ $1 == /* || $1 == *.conf ]] || libremessages panic || exit 1 # $EXIT_FAILURE
	local files envvars
	if [[ $1 == /* ]]; then
		files=("$1")
		envvars=()
		shift
	else
		local slug=${1%.conf}
		shift
		readarray -t files < <(list_files "$slug")
		readarray -t envvars < <(list_envvars "$slug")
	fi
	local var file

	# Save the existing versions at _VARNAME
	for var in "${envvars[@]}"; do
		[[ -n ${!var:-} ]] && eval "local _$var=\${$var}"
	done

	# Load the files
	for file in "${files[@]}"; do
		if [[ -r $file ]]; then
			. "$file" || return 6 # $EXIT_NOTCONFIGURED
		fi
	done

	# Restore the _SAVED versions
	for var in "${envvars[@]}"; do
		eval "$var=\${_$var:-\${$var:-}}"
	done

	# Verify that the variables we need were set
	declare -i ret=0 # $EXIT_SUCCESS
	for var in "$@"; do
		if [[ -z ${!var:-} ]]; then
			if [[ ${#files[@]} -gt 1 ]]; then
				libremessages _l print "Configure '%s' in one of:" "$var"
				printf '  -> %s\n' "${files[@]}"
			else
				libremessages _l print "Configure '%s' in '%s'" "$var" "${files[0]}"
			fi
			ret=6 # $EXIT_NOTCONFIGURED
		fi
	done >&2
	return $ret
}

# Usage: get_var <slug> <var_name> [ <default_value> ]
# Does not work with arrays
get_var() (
	set +euE
	local slug=$1
	local setting=$2
	local default=$3
	load_conf "$slug.conf"
	printf '%s' "${!setting:-${default}}"
)

# Usage: set_var <slug> <var_name> <value>
# Does not work with arrays
set_var() {
	local slug=$1
	local key=$2
	local val=$3
	local file
	while read -r file; do
		if [[ -w $file ]]; then
			sed -i "/^\s*$key=/d" "$file"
			printf '%s=%q\n' "$key" "$val" >>"$file"
			return 0 # $EXIT_SUCCESS
		fi
	done < <(list_files "$slug" | tac)
	return 1 # $EXIT_FAILURE
}

## PKGBUILD loading (not configuration, per se) ##

unset_PKGBUILD() {
	# This routine is based primarily off of the PKGBUILD(5) man-page,
	# version 4.2.0, dated 2014-12-31

	# This is kinda weird, but everything is more readable with
	# this as a utility function, but I didn't want to introduce a
	# new global function, so I just introduced it with the name
	# of a function that we get to unset anyway.  So it can't
	# clash with anything!
	mksource() {
		# For each arg, `unset -v` all variables matching ${arg} and ${arg}_*
		local v
		for v in "$@"; do
			# shellcheck disable=228
			unset -v "$v" $(declare -p | sed -En "s/^declare -\S+ (${v}_[a-zA-Z0-9_]*)=.*/\1/p")
		done
	}

	# From the "OPTIONS AND DIRECTIVES" section (in order of mention)
	unset -v pkgname pkgver
	unset -f pkgver
	unset -v pkgrel pkgdesc epoch url license install changelog

	mksource source
	unset -v validpgpkeys noextract
	local sums=("${known_hash_algos[@]/%/sums}")
	mksource "${sums[@]}"

	unset -v groups arch backup
	mksource depends makedepends checkdepends optdepends
	mksource conflicts provides replaces
	unset -v options

	# From the "PACKAGING FUNCTIONS" section (in order of mention)
	unset -f package prepare build check

	# From the "PACKAGE SPLITTING" section
	unset -f $(declare -f | sed -n 's/^\(package_\S*\) ()\s*$/\1/p')
	unset -v pkgbase

	# Parabola makepkg extensions (aka: mksource)
	# These are used by `librefetch` to generate an FSDG-fit source-ball,
	# before the prepare() funtion runs,
	# instead of the standard --allsource source-ball,
	# which is rolled before the prepare() funtions runs.
	unset -v mksource mknoextract "${sums[@]/#/mk}"
	unset -v mkdepends
	unset -f mksource
}

load_PKGBUILD() {
	local file=${1:-./PKGBUILD}
	unset_PKGBUILD
	# shellcheck disable=2034
	CARCH="$(get_var makepkg CARCH "$(uname -m)")"
	. "$file"
	pkgbase=${pkgbase:-${pkgname[0]}}
}
