#!/bin/bash
###############################################################################
#                                                                             #
# Pakfire - The IPFire package management system                              #
# Copyright (C) 2021 Pakfire development team                                 #
#                                                                             #
# 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 3 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/>.       #
#                                                                             #
###############################################################################

error() {
	echo "$@" >&2
}

pkgconfig_requires() {
	local file="${1}"

	# Require the pkgconfig package
	echo "pkgconfig"

	local n r v
	while read -r n r v; do
		echo "pkgconfig(${n}) ${r} ${v}"
	done < <(pkg-config --print-requires --print-requires-private "${file}" 2>/dev/null)

	return 0
}

find_elf_interpreter() {
	local file="${1}"
	local filelist="${2}"

	# Don't find interpreters for non-executable files
	if [ ! -x "${file}" ]; then
		return 0
	fi

	local interpreter="$(readelf -l "${file}" 2>/dev/null | \
		grep "program interpreter" | tr -d "]" | awk '{ print $NF }')"

	# Only add interpreter if it isn't part of this package
	if [ -n "${interpreter}" ]; then
		echo "${interpreter}"
	fi

	return 0
}

find_weak_symbols() {
	local file="${1}"

	local suffix

	# Is this a 64 bit file?
	if file -L "${file}" 2>/dev/null | grep -q "ELF 64-bit"; then
		suffix="(64bit)"
	fi

	# List all weak symbol versions
	objdump -p "${file}" 2>/dev/null | \
		awk \
			'BEGIN { START=0; LIBNAME=""; }
			/^$/ { START=0; }
			/^Dynamic Section:$/ { START=1; }
			(START==1) && /NEEDED/ {
				if ("'${suffix}'" != "") {
					sub(/$/, "()'${suffix}'", $2);
				}
				print $2;
			}
			(START==2) && /^[A-Za-z]/ { START=3; }
			/^Version References:$/ { START=2; }
			(START==2) && /required from/ {
				sub(/:/, "", $3);
				LIBNAME=$3;
			}
			(START==2) && (LIBNAME!="") && ($4!="") {
				print LIBNAME "(" $4 ")'${suffix}'";
			}'

	return 0
}

find_script_interpreter() {
	local file="${1}"

	# Don't find interpreters for non-executable files
	if [ ! -x "${file}" ]; then
		return 0
	fi

	local first_line="$(grep -q "^#!" "${file}" && head -n1 "${file}")"

	# Skip files that are not scripts
	if [ "${first_line:0:2}" != "#!" ]; then
		return 0
	fi

	local interpreter="${first_line:2}"

	# Ignore anything that does not have an absolute path
	if [ "${first_line:3:1}" != "/" ]; then
		return 0
	fi

	case "${interpreter}" in
		*/perl)
			# XXX process perl files here
			;;
	esac

	echo "${interpreter}"
	return 0
}

main() {
	local buildroot="${1}"

	# Check if BUILDROOT exists
	if [ ! -d "${buildroot}" ]; then
		error "BUILDROOT (${buildroot}) does not exist"
		return 1
	fi

	# Tell pkg-config to search in the newly created files, too
	export PKG_CONFIG_PATH="${buildroot}/usr/lib64/pkgconfig:${buildroot}/usr/lib/pkgconfig:${buildroot}/usr/share/pkgconfig"

	local file
	while read -r file; do
		# Filter out what we don't need
		case "${file}" in
			# Debug files
			/usr/lib/debug/*|/usr/src/debug/*)
				continue
				;;

			# Skip gconv
			/usr/lib*/gconv/*)
				continue
				;;

			# Skip all kernel modules
			*.ko)
				continue
				;;
		esac

		# Make the full path
		local path="${buildroot}${file}"

		# Process unresolvable symlinks
		if [ -L "${path}" ]; then
			local link="$(readlink -m "${path}")"

			# Make link relative to buildroot
			link="${link#${buildroot}}"

			# If the destination is not in this package, we create a dependency for it
			echo "${link}"

			# We do not process symlinks any further
			continue
		fi

		# Process special files
		case "${file}" in
			# pkg-config
			*.pc)
				# Query requires of the package
				pkgconfig_requires "${path}"
				;;

			# Python
			/usr/lib*/python*/*)
				# Fall through for all python files
				;;

			# Catch the module directory
			/usr/lib*/python[0-9]\.[0-9]*)
				# This will only get the python directory
				file="$(basename "${file}")"

				# Strip the python version from the string
				python_version="${file#python}"

				if [ -n "${python_version}" ]; then
					echo "python-abi = ${python_version}"
				fi
				;;
		esac

		# Skip anything that isn't a regular file
		if [ ! -f "${path}" ]; then
			continue
		fi

		# Is this an ELF file?
		if file -L "${path}" 2>/dev/null | grep -q "ELF"; then
			# Find the ELF interpreter
			if ! find_elf_interpreter "${path}" "${filelist}"; then
				return 1
			fi

			# Find weak symbols
			if ! find_weak_symbols "${path}"; then
				return 1
			fi

			continue
		fi

		# Add script interpreters
		if ! find_script_interpreter "${path}"; then
			return 1
		fi
	done | sort -u

	return 0
}

main "$@" || exit $?
