#!/bin/sh
#
# Spk-ls - List SliTaz packages and files. Read the README before adding or
# modifying any code in spk!
#
# Copyright (C) SliTaz GNU/Linux - BSD License
# Author: See AUTHORS files
#
. /usr/lib/slitaz/libspk.sh

#
# Functions
#

# Help and usage
usage() {
	name=$(basename $0)
	cat << EOT

$(boldify $(gettext "Usage:")) $name [packages|--options]

$(gettext "List packages or installed files by packages")

$(boldify $(gettext "Options:"))
  --count      $(gettext "Display the number of installed packages")
  --mirror     $(gettext "List all the packages on mirror")
  --extra      $(gettext "List packages on extra mirrors ")
  --blocked    $(gettext "List all blocked packages")
  --short      $(gettext "Short packages list format")
  --modifiers  $(gettext "List package modifiers")
  --raw        $(gettext "Plain TSV, scriptable, stable format")
  --trame      $(gettext "JSON object on stdout, aligned with trame conventions")
  --root=      $(gettext "Set the root file system path")
  --color=NB   $(gettext "Set package name color in list")
  --debug      $(gettext "Display some useful debug information")

$(boldify $(gettext "Examples:"))
  $name package1 package2 packageN
  $name --short --color=33

EOT
	exit 0
}

#
# Output mode + small JSON helpers
#

output=human
[ "$raw" ] && output=raw
[ "$trame" ] && output=trame

# JSON-escape (busybox sed): same shape as spk-search/spk-doctor.
json_escape() {
	printf '%s' "$1" | sed \
		-e 's/\\/\\\\/g' \
		-e 's/"/\\"/g' \
		-e 's/	/\\t/g' \
		-e 's/\r/\\r/g' \
		-e ':a;N;$!ba;s/\n/\\n/g'
}

# Emit one package object/row. trame uses a comma prefix mechanism
# (see open_trame / close_trame) so the caller manages [ , ] framing.
# Args: source pkg ver cat desc installed(true|false)
emit_pkg() {
	local _src="$1" pkg="$2" ver="$3" cat_="$4" desc="$5" inst="$6"
	case "$output" in
	raw)
		printf '%s\t%s\t%s\t%s\t%s\t%s\n' \
			"$_src" "$pkg" "$ver" "$cat_" "$inst" "$desc"
		;;
	trame)
		[ "$first" ] || printf ','
		first=
		printf '{"package":"%s","version":"%s","category":"%s","short_desc":"%s","installed":%s}' \
			"$(json_escape "$pkg")" "$(json_escape "$ver")" \
			"$(json_escape "$cat_")" "$(json_escape "$desc")" "$inst"
		;;
	esac
}

# Open / close the trame envelope. Source is one of:
# installed | mirror | extra | blocked
open_trame() {
	[ "$output" = trame ] || return 0
	printf '{"command":"ls","source":"%s","packages":[' "$1"
	first=yes
}
close_trame() {
	[ "$output" = trame ] || return 0
	printf '],"count":%s}\n' "${1:-0}"
}

#
# Handle --options
#

for opt in $@
do
	case "$opt" in
		*usage|*help) usage ;;
		--count)
			# --raw/--trame emit machine counts; human keeps the labelled
			# lines. (count_installed/count_mirrored live in libspk and print
			# human text, so compute locally for the machine formats.)
			if [ "$output" = human ]; then
				count_installed
				count_mirrored
			else
				ni=$(ls "$installed" 2>/dev/null | wc -l)
				nm=0; [ -f "$pkgsmd5" ] && nm=$(wc -l < "$pkgsmd5")
				if [ "$output" = raw ]; then
					printf 'count\tinstalled\t%s\n' "$ni"
					printf 'count\tmirror\t%s\n' "$nm"
				else
					printf '{"command":"ls","installed":%s,"mirror":%s}\n' \
						"$ni" "$nm"
				fi
			fi
			exit 0 ;;
		--mirror)
			if [ "$output" = human ]; then
				newline
				boldify $(gettext "Mirror") $(cat $mirrorurl)
				separator
				read_pkgsinfo $pkgsinfo
				separator
				boldify $(count_mirrored)
				newline && exit 0
			fi
			open_trame mirror
			n=0
			while IFS="	" read pkg ver cat_ desc rest; do
				[ -z "$pkg" ] && continue
				inst=false
				is_package_installed "$pkg" && inst=true
				emit_pkg mirror "$pkg" "$ver" "$cat_" "$desc" "$inst"
				n=$((n + 1))
			done < "$pkgsinfo"
			close_trame "$n"
			exit 0 ;;
		--extra)
				[ -d "$extradb" ] || exit 1
				if [ "$output" != human ]; then
					# Machine formats: one row/object per package, source =
					# the extra repo name (same shape as --mirror).
					open_trame extra
					n=0
					for extra in $extradb/*; do
						[ -f "$extra/packages.info" ] || continue
						esrc=$(basename "$extra")
						while IFS="	" read pkg ver cat_ desc rest; do
							[ -z "$pkg" ] && continue
							inst=false
							is_package_installed "$pkg" && inst=true
							emit_pkg "$esrc" "$pkg" "$ver" "$cat_" "$desc" "$inst"
							n=$((n + 1))
						done < "$extra/packages.info"
					done
					close_trame "$n"
					exit 0
				fi
				for extra in $extradb/*
				do
					newline
					boldify $(gettext "Extra mirror")
					if [ ! -f "$extra/packages.info" ]; then
						echo "URL: $(cat $extra/mirror)"
						gettext "Missing:"; colorize 31 " packages.info"
						continue
					fi
					separator
					read_pkgsinfo $extra/packages.info
					separator
					boldify $(echo -n $(cat $extra/packages.$SUM | wc -l))
					gettext "packages in:"; echo " $(basename $extra)"
					newline
				done
				exit 0 ;;
		--blocked)
			if [ "$output" = human ]; then
				if [ -s "$blocked" ]; then
					cat $blocked
				else
					gettext "No blocked packages"; newline
				fi
				exit 0
			fi
			open_trame blocked
			n=0
			if [ -s "$blocked" ]; then
				while IFS= read -r pkg; do
					[ -z "$pkg" ] && continue
					inst=false
					is_package_installed "$pkg" && inst=true
					emit_pkg blocked "$pkg" "" "" "" "$inst"
					n=$((n + 1))
				done < "$blocked"
			fi
			close_trame "$n"
			exit 0 ;;
		--short)
			if [ "$output" = human ]; then
				newline
				echo -n $(boldify $(gettext "Package"))
				indent 28 $(boldify $(gettext "Version"))
				separator
				for pkg in $(ls -1 $installed)
				do
					source_receipt $installed/$pkg/receipt
					echo $(colorize 32 $pkg) $(indent 28 "$VERSION")
				done
				separator
				boldify $(count_installed)
				newline
				exit 0
			fi
			open_trame installed
			n=0
			for pkg in $(ls -1 $installed); do
				unset_receipt
				source_receipt $installed/$pkg/receipt
				emit_pkg installed "$PACKAGE" \
					"${VERSION}${EXTRAVERSION}" \
					"$CATEGORY" "$SHORT_DESC" true
				n=$((n + 1))
			done
			close_trame "$n"
			exit 0 ;;
		--*) continue ;;
		*)
			# List installed files by the package.
			count=0; rc=0
			for pkg in $@
			do
				# $@ still carries any flags (e.g. `spk-ls foo --verbose`).
				case "$pkg" in --*) continue ;; esac
				# A requested package that isn't installed must be reported and
				# make spk-ls exit non-zero — the old silent `continue` gave
				# exit 0 with no output, so a script couldn't tell "not
				# installed" from "installed but shipping no files".
				if ! is_package_installed "$pkg"; then
					gettext "Package not installed:"; colorize 31 " $pkg"
					newline; rc=1; continue
				fi
				[ -f "$installed/$pkg/files.list" ] || continue
				nb=$(cat $installed/$pkg/files.list | wc -l)
				count=$(($count + 1))
				[ "$count" = 1 ] && newline
				# List modifiers
				if [ "$modifiers" ]; then
					modifiers=$installed/$pkg/modifiers
					if [ -f "$modifiers" ]; then
						boldify $(gettext "Modifiers for") $pkg
						separator
						cat $modifiers
						separator && newline
					else
						echo $(boldify $pkg) $(gettext "package was not modified")
					fi
					continue
				fi
				boldify $(gettext "Installed files by") $pkg
				separator
				cat $installed/$pkg/files.list
				separator
				gettext "Installed files by"; echo -n " $pkg: "
				colorize 32 "$nb" && newline
			done
			exit $rc ;;
	esac
done

#
# Parse all installed pkgs receipt.
#

count=0

if [ "$output" = human ]; then
	newline
	boldify $(gettext "Installed packages")
	separator
	for pkg in $installed/*
	do
		# Skip a receipt-less ghost dir (half-removed / corrupt DB, repaired
		# by spk-doctor): source_receipt would print "Missing receipt: …" to
		# stdout and leave PACKAGE unset, listing a bogus empty package.
		[ -f "$pkg/receipt" ] || continue
		unset_receipt
		source_receipt $pkg/receipt
		count=$(($count + 1))
		[ "$count" != 1 ] && newline
		gettext "Package    :"; colorize 32 " $PACKAGE"
		receipt_info
	done
	separator
	boldify $(count_installed) && newline
	exit 0
fi

# raw / trame: emit the installed list as a flat array.
open_trame installed
for pkg in $installed/*; do
	# Skip ghost dirs: without this, source_receipt's "Missing receipt: …"
	# text lands INSIDE the JSON array (invalid) / as a non-TSV line in --raw,
	# plus a package="" entry from the unset PACKAGE.
	[ -f "$pkg/receipt" ] || continue
	unset_receipt
	source_receipt $pkg/receipt
	emit_pkg installed "$PACKAGE" \
		"${VERSION}${EXTRAVERSION}" \
		"$CATEGORY" "$SHORT_DESC" true
	count=$((count + 1))
done
close_trame "$count"
exit 0
