#!/bin/bash
# SPDX-License-Identifier: MIT
#
# packages - collect state of installed packages.
#
# Copyright IBM Corp. 2023
#
# Usage: packages [<datafile>]
#
# Get version of packages specified in datafile. Filter out packages that are
# not installed. Map package names according to rules in file 'packages.alias'.
#
# Example input (datafile):
#
#   packages:
#     bash:
#     perl:
#     unknown:
#
# Example output (stdout):
#
#   packages:
#     bash: 4.0
#     perl: 5.8
#


function die() {
        echo -n "${0##*/}: "
        echo "$@" >&2
        exit 1
}

DATAFILE="$1"

# Compare versions a and b using comparison operator op (< > <= >= == !=).
function cmp_ver() {
	local ver_a="$1" ver_b="$2" op="$3" comp_a comp_b a b i

	# Split OS version
	IFS=. read -ra comp_a <<<"$ver_a"
	IFS=. read -ra comp_b <<<"$ver_b"

	# Compare version components
	for (( i=0; i < ${#comp_a[@]} || i < ${#comp_b[@]}; i++)) ; do
		a="${comp_a[$i]:-0}"
		b="${comp_b[$i]:-0}"

		[[ "$a" != "$b" ]] && break
	done

	# Force base 10 comparison to handle leading zeroes
	eval "(( 10#$a $op 10#$b ))"
}

function match_os() {
	local spec="$1" os ver op="=="

	IFS=- read -r os ver <<<"$spec"
	if [[ "$ver" =~ \+$ ]] ; then
		op=">="
		ver="${ver%+}"
	fi

	# Match OS ID
	[[ "$TELA_OS_ID" == "$os" ]] || return 1
	[[ -z "$ver" ]] && return 0

	cmp_ver "$TELA_OS_VERSION" "$ver" "$op"
}

# Adjust package name for this OS
function get_alias() {
	local IFS var="$1" name="$2" pkg list entry os alt

	while read -r pkg list ; do
		[[ "$pkg" == "$name:" ]] || continue
		for entry in $list ; do
			os=${entry%%:*}
			alt=${entry#*:}
			alt=${alt//|/ }
			if match_os "$os" ; then
				eval "$var=($alt)"
				return
			fi
		done
	done < packages.alias

	eval "$var=$name"
}

# Check status and version of specified package using RPM
function check_rpm() {
	local IFS name="$1" name2 pkg buffer

	# Get alternate name for package on this OS
	get_alias name2 $name

	#  Get package version
	for pkg in "${name2[@]}"; do
		read buffer < <(rpm --query --queryformat '%{VERSION}\n' \
				$pkg 2>/dev/null) || continue
		if ! [[ "$buffer" =~ "not installed" ]] ; then
			# Emit result as YAML
			echo "  $name: $buffer"
			return
		fi
	done
}

# Check status and version of specified package using DPKG
function check_dpkg() {
	local IFS name="$1" name2 pkg buffer

	# Get alternate name for package on this OS
	get_alias name2 $name

	#  Get package version
	for pkg in "${name2[@]}"; do
		read buffer < <(dpkg-query --show --showformat '${Version}\n' \
					${pkg} 2>/dev/null) || continue
		if [[ -n "$buffer" ]] ; then
			# Emit result as YAML
			echo "  $name: $buffer"
			return
		fi
	done
}

function get_state() {
	local check line id

	# Determine package manager
	check=
	case "$TELA_OS_ID" in
		rhel|sles|suse|fedora|linuxonzdriver)
			check=check_rpm
			;;
		ubuntu|debian)
			check=check_dpkg
			;;
		*)
			die "Unsupported OS '$TELA_OS_ID'"
			;;
	esac

	IFS=$'\n'

	# Header
	read line
	[[ -n "$line" ]] && echo "$line"

	# Check each package
	while read -r line ; do
		id=${line:2}
		id="${id%%:*}"

		$check $id
	done
}

if [ -n "$DATAFILE" ] ; then
	# Get state for resources specified in datafile
	get_state <"$DATAFILE"
fi

exit 0
