# Copyright 1999-2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

# @ECLASS: texlive-common.eclass
# @MAINTAINER:
# tex@gentoo.org
# @AUTHOR:
# Original Author: Alexis Ballier <aballier@gentoo.org>
# @SUPPORTED_EAPIS: 7 8
# @BLURB: Provide various functions used by both texlive-core and texlive modules
# @DESCRIPTION:
# Purpose: Provide various functions used by both texlive-core and texlive
# modules.
#
# Note that this eclass *must* not assume the presence of any standard tex too

case ${EAPI} in
	7)
		inherit eapi8-dosym
		;;
	8) ;;
	*) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
esac

inherit edo

if [[ -z ${_TEXLIVE_COMMON_ECLASS} ]]; then
_TEXLIVE_COMMON_ECLASS=1

# @ECLASS_VARIABLE: CTAN_MIRROR_URL
# @USER_VARIABLE
# @DESCRIPTION:
# This variable can be used to set the CTAN mirror that will be used to fetch
# CTAN artifacts. Note that this mirror is usually only used as fallback
# in case the Gentoo mirrors do not hold the requested files.
#
# Only Gentoo TeX developers may want to set this.
#
# Example:
# @CODE
# CTAN_MIRROR_URL='https://ftp.fau.de/ctan/' emerge -1v app-text/texlive-core
# @CODE
: "${CTAN_MIRROR_URL:="https://mirrors.ctan.org"}"

# @ECLASS_VARIABLE: TEXLIVE_SCRIPTS_W_FILE_EXT
# @DEFAULT_UNSET
# @DESCRIPTION:
# If set, contains a space separated list of script names that should be
# linked including their file extensions, i.e., without stripping
# potentially existing filename extensions from the link's name.

# @FUNCTION: texlive-common_handle_config_files
# @DESCRIPTION:
# Has to be called in src_install after having installed the files in ${D}
# This function will move the relevant files to /etc/texmf and symlink them
# from their original location. This is to allow easy update of texlive's
# configuration.
# Called by app-text/texlive-core and texlive-module.eclass.
texlive-common_handle_config_files() {
	local texmf_path
	# Starting with TeX Live 2023, we install in texmf-dist, where a
	# distribution-provided TeX Live installation is supposed to be,
	# instead of texmf.
	if ver_test -ge 2023; then
		texmf_path=/usr/share/texmf-dist
	else
		texmf_path=/usr/share/texmf
	fi

	# Handle config files properly
	[[ -d ${ED}${texmf_path} ]] || return
	cd "${ED}${texmf_path}" || die

	while read -r f; do
		if [[ ${f#*config} != "${f}" || ${f#doc} != "${f}" || ${f#source} != "${f}" || ${f#tex} != "${f}" ]] ; then
			continue
		fi
		local rel_dir
		rel_dir="$(dirname "${f}")"

		dodir "/etc/texmf/${rel_dir}.d"
		einfo "Moving (and symlinking) ${EPREFIX}${texmf_path}/${f} to ${EPREFIX}/etc/texmf/${rel_dir}.d"
		mv "${ED}/${texmf_path}/${f}" "${ED}/etc/texmf/${rel_dir}.d" || die "mv ${f} failed."

		local dosym=dosym
		[[ ${EAPI} == 7 ]] && dosym=dosym8
		${dosym} -r "/etc/texmf/${rel_dir}.d/$(basename "${f}")" "${texmf_path}/${f}"
	done < <(find . -name '*.cnf' -type f -o -name '*.cfg' -type f | sed -e "s:\./::g")
}

# @FUNCTION: texlive-common_is_file_present_in_texmf
# @DESCRIPTION:
# Return if a file is present in the texmf tree
# Call it from the directory containing texmf and texmf-dist
# Called by app-text/texlive-core.
texlive-common_is_file_present_in_texmf() {
	local mark="${T}/${1}.found"
	if [[ -d texmf ]]; then
		find texmf -name "${1}" -exec touch "${mark}" {} + || die
	fi

	if [[ -d texmf-dist ]]; then
		find texmf-dist -name "${1}" -exec touch "${mark}" {} + || die
	fi
	[ -f "${mark}" ]
}

# @FUNCTION: texlive-common_do_symlinks
# @USAGE: <src> <dest>
# @DESCRIPTION:
# Mimic the install_link function of texlinks
#
# Should have the same behavior as the one in /usr/bin/texlinks
# except that it is under the control of the package manager
# Note that $1 corresponds to $src and $2 to $dest in this function
# ( Arguments are switched because texlinks main function sends them switched )
# This function should not be called from an ebuild, prefer etexlinks that will
# also do the fmtutil file parsing.
# Called by texlive-common.eclass and texlive-module.eclass.
texlive-common_do_symlinks() {
	while [[ ${#} != 0 ]]; do
		case ${1} in
			cont-??|metafun|mptopdf)
				einfo "Symlink ${1} skipped (special case)"
				;;
			mf)
				einfo "Symlink ${1} -> ${2} skipped (texlive-core takes care of it)"
				;;
			*)
				if [[ ${1} == "${2}" ]]; then
					einfo "Symlink ${1} -> ${2} skipped"
				elif [[ -e ${ED}/usr/bin/${1} || -L ${ED}/usr/bin/${1} ]]; then
					einfo "Symlink ${1} skipped (file exists)"
				else
					einfo "Making symlink from ${1} to ${2}"
					dosym "${2}" "/usr/bin/${1}"
				fi
				;;
		esac
		shift; shift;
	done
}

# @FUNCTION: etexlinks
# @USAGE: <file>
# @DESCRIPTION:
# Mimic texlinks on a fmtutil format file
#
# $1 has to be a fmtutil format file like fmtutil.cnf
# etexlinks foo will install the symlinks that texlinks --cnffile foo would have
# created. We cannot use texlinks with portage as it is not DESTDIR aware.
# (It would not fail but will not create the symlinks if the target is not in
# the same dir as the source)
# Also, as this eclass must not depend on a tex distribution to be installed we
# cannot use texlinks from here.
# Called by texlive-module.eclass.
etexlinks() {
	# Install symlinks from formats to engines
	texlive-common_do_symlinks $(sed '/^[      ]*#/d; /^[      ]*$/d' "$1" | awk '{print $1, $2}')
}

# @FUNCTION: dobin_texmf_scripts
# @USAGE: <file1> [file2] ...
# @DESCRIPTION:
# Symlinks a script from the texmf tree to /usr/bin. Requires permissions to be
# correctly set for the file that it will point to.
# Called by app-text/epspdf and texlive-module.eclass.
dobin_texmf_scripts() {
	while [[ ${#} -gt 0 ]] ; do
		# -l: TexLive target links are always lowercase.
		local -l trg

		# Get the basename of the script.
		trg="${1##*/}"

		# Only strip the filename extensions if trg is not listed in TEXLIVE_SCRIPTS_W_FILE_EXT.
		if ! has "${trg}" ${TEXLIVE_SCRIPTS_W_FILE_EXT}; then
			trg="${trg%.*}"
		fi

		einfo "Installing ${1} as ${trg} bin wrapper"
		[[ -x ${ED}/usr/share/${1} ]] || die "Trying to install a non existing or non executable symlink to /usr/bin: ${1}"
		dosym "../share/${1}" "/usr/bin/${trg}"
		shift
	done
}

# @FUNCTION: etexmf-update
# @DESCRIPTION:
# Runs texmf-update if it is available and prints a warning otherwise. This
# function helps in factorizing some code.  Useful in ebuilds' pkg_postinst and
# pkg_postrm phases.
# Called by app-text/dvipsk, app-text/texlive-core, dev-libs/kpathsea, and
# texlive-module.eclass.
etexmf-update() {
	if has_version 'app-text/texlive-core' ; then
		if [[ -z ${ROOT} && -x "${EPREFIX}"/usr/sbin/texmf-update ]] ; then
			"${EPREFIX}"/usr/sbin/texmf-update
			local res="${?}"
			if [[ "${res}" -ne 0 ]] &&
				   { [[ ${CATEGORY} != dev-texlive ]] || ver_test -ge 2023; } then
				die -n "texmf-update returned non-zero exit status ${res}"
			fi
		else
			ewarn "Cannot run texmf-update for some reason."
			ewarn "Your texmf tree might be inconsistent with your configuration"
			ewarn "Please try to figure what has happened"
		fi
	fi
}

# @FUNCTION: efmtutil-sys
# @DESCRIPTION:
# Runs fmtutil-sys if it is available and prints a warning otherwise. This
# function helps in factorizing some code. Used in ebuilds' pkg_postinst to
# force a rebuild of TeX formats.
efmtutil-sys() {
	if has_version 'app-text/texlive-core' ; then
		if [[ -z ${ROOT} && -x "${EPREFIX}"/usr/bin/fmtutil-sys ]] ; then
			edob -m "Rebuilding TexLive formats" \
				 -l fmtutils-sys-all \
				 "${EPREFIX}"/usr/bin/fmtutil-sys --all
		else
			ewarn "Cannot run fmtutil-sys for some reason."
			ewarn "Your formats might be inconsistent with your installed ${PN} version"
			ewarn "Please try to figure what has happened"
		fi
	fi
}

# @FUNCTION: texlive-common_append_to_src_uri
# @DESCRIPTION:
# Takes the name of a variable as input.  The variable must contain a
# list of texlive packages.  Every texlive package in the variable is
# transformed to an URL and appended to SRC_URI.
texlive-common_append_to_src_uri() {
	local tl_uri=( ${!1} )

	# Starting from TeX Live 2009, upstream provides .tar.xz modules.
	local tl_pkgext=tar.xz

	local tl_uri_prefix="https://dev.gentoo.org/~@dev@/distfiles/texlive/tl-"
	local tl_2023_uri_prefix="https://dev.gentoo.org/~@dev@/distfiles/texlive/"

	local tl_dev
	# If the version is less than 2023 and the package is the
	# dev-texlive category, we fallback to the old SRC_URI layout. With
	# the 2023 bump, packages outside the dev-texlive category start to
	# inherit texlive-common.eclass.
	if ver_test -lt 2023 && [[ ${CATEGORY} == dev-texlive ]]; then
		local texlive_lt_2023_devs=( zlogene dilfridge sam )
		local tl_uri_suffix="-${PV}.${tl_pkgext}"

		tl_uri=( "${tl_uri[@]/%/${tl_uri_suffix}}" )
		for tl_dev in "${texlive_lt_2023_devs[@]}"; do
			SRC_URI+=" ${tl_uri[*]/#/${tl_uri_prefix/@dev@/${tl_dev}}}"
		done
	else
		local texlive_ge_2023_devs=( flow )
		local tl_mirror="${CTAN_MIRROR_URL%/}/systems/texlive/tlnet/archive/"

		tl_uri=( "${tl_uri[@]/%/.${tl_pkgext}}" )
		SRC_URI+=" ${tl_uri[*]/#/${tl_mirror}}"
		for tl_dev in "${texlive_ge_2023_devs[@]}"; do
			SRC_URI+=" ${tl_uri[*]/#/${tl_2023_uri_prefix/@dev@/${tl_dev}}}"
		done
	fi
}

# @FUNCTION: texlive-common_update_tlpdb
# @DESCRIPTION:
# Update the TexLive package database at /usr/share/tlpkg/texlive.tlpdb.
texlive-common_update_tlpdb() {
	[[ -v TL_PV && ${TL_PV} -lt 2023 ]] && return

	# If we are updating this package, then there is no need to update
	# the tlpdb in postrm, as it will be again updated in postinst.
	[[ ${EBUILD_PHASE} == postrm && -n ${REPLACED_BY_VERSION} ]] && return

	local tlpkg="${EROOT}"/usr/share/tlpkg
	local tlpobj="${tlpkg}"/tlpobj
	local tlpdb="${tlpkg}"/texlive.tlpdb

	ebegin "Regenerating TexLive package database"

	local new_tlpdb="${T}"/texlive.tlpdb

	touch "${new_tlpdb}" || die

	if [[ -d "${tlpobj}" ]]; then
		# The "sed -s '$G' below concatenates all tlpobj files separated
		# by a newline.
		find "${tlpobj}" -maxdepth 1 -type f -name "*.tlpobj" -print0 |
			sort -z |
			xargs -0 --no-run-if-empty sed -s '$G' >> "${new_tlpdb}"
		assert "generating tlpdb failed"
	fi

	if [[ -f ${tlpdb} ]]; then
		cmp -s "${new_tlpdb}" "${tlpdb}"
		local ret=$?
		case ${ret} in
			# content equal
			0)
				# Nothing to do, return.
				eend 0
				return
				;;
			# content differs
			1)
				;;
			# cmp failed with an error
			*)
				eend ${ret} "comparing new and existing tlpdb failed (exit status: ${ret})"
				die
				;;
		esac
	fi

	mv "${new_tlpdb}" "${tlpdb}"
	eend $? "moving tlpdb into position failed (exit status: ${?})" || die

	if [[ ! -s ${tlpdb} ]]; then
		rm "${tlpdb}" || die
	fi
}

fi