#!/bin/sh
#
# Spk - The SliTaz Packages toolset. 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
#. lib/libspk.sh

#
# Functions
#

VERSION=2.3

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

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

$(gettext "SliTaz Packages toolset v$VERSION")

$(boldify $(gettext "Commands:"))
  info        $(gettext "Display path, mirror and other stats")
  activity    $(gettext "Display packages activities (--since=DATE, --pkg=N)")
  history     $(gettext "Activity history for one package: spk history <pkg>")
  clean       $(gettext "Clean cache and temporary files")
  reconf      $(gettext "Reconfigure installed packages")

$(boldify $(gettext "Options:"))
  --add       $(gettext "Install packages if mirrored")
  --rm        $(gettext "Remove installed packages")
  --get       $(gettext "Download packages specified on cmdline")
  --block     $(gettext "Add packages to the blocked list")
  --unblock   $(gettext "Remove packages from the blocked list")
  --log       $(gettext "Show package install and upgrade log")
  --cache     $(gettext "Used with --get to download in cache")
  --forced    $(gettext "Force packages reinstall or download")
  --root=     $(gettext "Set the root file system path")
  --debug     $(gettext "Display some useful debug information")

$(boldify $(gettext "Examples:"))
  $name package1 package2 packageN
  $name package package2 --block

EOT
	exit 0
}

#
# Commands and exit
#

case "$1" in
	""|-h|--help|usage|help) usage ;;
	info)
		cache="$(du -sh $CACHE_DIR | awk '{print $1 " " $2}')"
		extra=$(ls $extradb | wc -l)
		newline
		boldify "Spk info"
		separator
		gettext "Architecture  :"; echo " $SLITAZ_ARCH"
		gettext "Database      :"; echo " $installed"
		gettext "Cache info    :"; echo " $cache"
		gettext "Mirror URL    :"; echo " $(cat $mirrorurl)"
		gettext "Extra mirrors :"; echo " $extra"
		count_installed
		count_mirrored
		separator && newline
		exit 0 ;;
	activity|history)
		# Flags: --lines=NB (default 18, 0 = all)
		#        --since=YYYY-MM-DD (lexical prefix)
		#        --pkg=NAME (whole-word match in action field)
		#        --raw / --trame (structured output)
		# `spk history <pkg>` is sugar for `spk activity --pkg=<pkg>`.
		cmd="$1"; shift
		if [ "$cmd" = history ]; then
			[ -z "$1" ] && { gettext "Usage: spk history <pkg>"; newline; exit 1; }
			pkg="$1"
		fi
		: ${lines=18}
		# Build a filter pipeline reading the activity log.
		buf=
		if [ -f "$activity" ]; then
			buf=$(cat "$activity")
			[ "$since" ] && buf=$(echo "$buf" | awk -v s="$since" '$1 >= s')
			[ "$pkg" ] && buf=$(echo "$buf" | grep -E ":[[:space:]]*[A-Za-z]+ package:[[:space:]]*${pkg}$")
			[ "$lines" -gt 0 ] 2>/dev/null && \
				buf=$(echo "$buf" | tail -n "$lines")
		fi
		# Convert one activity line into 4 TSV fields:
		#   date    time    action    package
		# Schema: "YYYY-MM-DD HH:MM : <Action> package: <name>"
		render_tsv() {
			echo "$buf" | awk -F' : ' '
				NF >= 2 {
					split($1, ts, " "); date = ts[1]; time = ts[2];
					split($2, rest, " package: ");
					action = rest[1]; pkg = rest[2];
					printf "%s\t%s\t%s\t%s\n", date, time, action, pkg;
				}'
		}
		case "${raw}${trame}" in
		""|*)
			# json_escape inline for the trame mode.
			;;
		esac
		if [ "$raw" ]; then
			render_tsv
		elif [ "$trame" ]; then
			printf '{"command":"activity","events":['
			first=1
			render_tsv | while IFS="	" read d t a p; do
				[ "$first" -eq 1 ] || printf ','
				first=0
				printf '{"date":"%s","time":"%s","action":"%s","package":"%s"}' \
					"$d" "$t" "$a" "$p"
			done
			# count is recomputed cheaply since the subshell consumed first.
			n=$(echo "$buf" | grep -c .)
			printf '],"count":%s}\n' "$n"
		else
			newline
			if [ "$cmd" = history ]; then
				boldify "Spk History: $pkg"
			else
				boldify "Spk Activity"
			fi
			separator
			[ -n "$buf" ] && echo "$buf"
			separator && newline
		fi
		exit 0 ;;
	clean)
		newline
		boldify "Spk Clean"
		separator
		# Never expand `rm -rf $CACHE_DIR/*` on an empty CACHE_DIR: a
		# partial/corrupt slitaz.conf that leaves it unset would make the
		# glob resolve to `/*`. The DB sanity check above only guards
		# PKGS_DB, so guard the cache paths here before deleting.
		if [ -z "$CACHE_DIR" ] || [ -z "$tmpdir" ]; then
			gettext "CACHE_DIR or tmpdir unset — refusing to clean"; newline
			exit 1
		fi
		size=$(du -sh $CACHE_DIR 2>/dev/null | awk '{print $1}')
		gettext "Cleaning cache:"; echo -n " $CACHE_DIR ($size)"
		rm -rf "$CACHE_DIR"/* && status
		gettext "Cleaning tmp  :"; echo -n " $(dirname $tmpdir)"
		rm -rf "$(dirname $tmpdir)" && status
		separator && newline
		exit 0 ;;
	reconf|reconfigure)
		shift
		packages="$@"
		[ "$all" ] && packages=$(ls $installed)
		for pkg in ${packages}
		do
			receipt="$installed/$pkg/receipt"
			[ ! -f "$receipt" ] && continue
			if grep -q ^post_install ${receipt}; then
				gettext "Configuring packages:"; echo " $pkg"
				. ${receipt}
				post_install
			fi
		done && exit 0 ;;
esac

#
# trame helpers: JSON output for `spk PKG` info queries
#

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

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'
}

# Comma-separated string -> JSON array of strings.
str_to_jsonarr() {
	local s="$1" w out=""
	for w in $s; do
		[ -z "$out" ] || out="${out},"
		out="${out}\"$(json_escape "$w")\""
	done
	printf '[%s]' "$out"
}

# Emit one package info record. Caller manages array framing.
# Args: source pkg installed version category short_desc web_site depends provide
emit_info() {
	local _src="$1" pkg="$2" inst="$3" ver="$4" cat_="$5" \
		desc="$6" web="$7" deps="$8" prov="$9"
	case "$output" in
	trame)
		[ "$info_first" ] || printf ','
		info_first=
		printf '{"package":"%s","installed":%s,"version":"%s","category":"%s","short_desc":"%s","web_site":"%s","depends":%s,"provides":%s,"source":"%s"}' \
			"$(json_escape "$pkg")" "$inst" \
			"$(json_escape "$ver")" "$(json_escape "$cat_")" \
			"$(json_escape "$desc")" "$(json_escape "$web")" \
			"$(str_to_jsonarr "$deps")" "$(str_to_jsonarr "$prov")" \
			"$_src"
		;;
	raw)
		printf 'info\t%s\t%s\t%s\t%s\t%s\t%s\n' \
			"$_src" "$pkg" "$inst" "$ver" "$cat_" "$desc"
		;;
	esac
}

if [ "$output" = trame ]; then
	printf '{"command":"info","results":['
	info_first=yes
fi

#
# Handle packages: spk package1 ... packageN
#

#debug "cmdline: $@"
count=0

for pkg in $@
do
	# Handle general: --options
	case " $@ " in
		*\ --get\ *)
			# We want: package-version.tazpkg
			package_full=$(full_package $pkg)
			mirrored_pkg $pkg
			[ "$mirrored" ] || continue
			[ "$count" = 0 ] && newline
			count=$(($count + 1))
			download $package_full $mirror
			unset_mirrored
			if [ ! "$cache" ]; then
				gettext "Moving package to current directory..."
				mv -f "$CACHE_DIR/$package_full" .
				status
			fi
			newline && continue ;;
	esac
	# Installed ?
	if [ -d "$installed/$pkg" ]; then
		# Handle: --options
		case " $@ " in
			*\ --block\ *)
				check_root
				[ -d "$installed/$pkg" ] || continue
				if grep -qs ^${pkg}$ $blocked; then
					echo $(boldify $pkg) $(gettext "is already blocked")
				else
					gettext "Blocking package:"; echo -n " $pkg"
					echo $pkg >> $blocked
					log "Blocked package: $pkg" && status
				fi
				continue ;;
			*\ --unblock\ *)
				check_root
				[ -d "$installed/$pkg" ] || continue
				if grep -qs ^${pkg}$ $blocked; then
					gettext "Unblocking package:"; echo -n " $pkg"
					sed -i /"^${pkg}$"/d $blocked
					log "Unblocked package: $pkg" && status
				else
					echo $(boldify $pkg) $(gettext "is not blocked")
				fi
				continue ;;
			*\ --rm\ *)
				is_package_installed $pkg || continue
				spk-rm $pkg --count=$count
				count=$(($count + 1))
				continue ;;
			*\ --log\ *)
				# Display package's log
				if [ -f "$logdir/$pkg/install.log" ]; then
					count=$(($count + 1))
					[ "$count" = "1" ] && newline
					colorize 36 $(gettext "Install log for:"; echo " $pkg")
					separator
					cat $logdir/$pkg/install.log
				else
					gettext "Any install log for:"; boldify " $pkg"
				fi
				if [ -f "$logdir/$pkg/up.log" ]; then
					colorize 36 $(gettext "Upgrade log for:"; echo " $pkg")
					separator
					cat $logdir/$pkg/up.log
				else
					colorize 36 $(gettext "Any upgrade log for:"; echo " $pkg")
					newline
				fi
				continue ;;
			*\ --extract\ *)
					newline
					echo $(boldify $(gettext "Extracting:")) $pkg
					separator ;;
			*\ --forced\ *)
				spk-add --forced $pkg --count=$count
				count=$(($count + 1))
				continue ;;
		esac
		count=$(($count + 1))
		unset_receipt
		source_receipt $installed/$pkg/receipt
		if [ "$output" = human ]; then
			[ "$count" = 1 ] && newline
			boldify $(gettext "Package") $pkg
			separator
			gettext "Status     :"; colorize 32 " installed"
			receipt_info
			separator && newline
		else
			emit_info installed "$PACKAGE" true \
				"${VERSION}${EXTRAVERSION}" \
				"$CATEGORY" "$SHORT_DESC" "$WEB_SITE" \
				"$DEPENDS" "$PROVIDE"
		fi
		continue
	fi
	# Mirrored ?
	mirrored_pkg $pkg
	if [ "$mirrored" ]; then
		# Handle mirrored: --options
		case " $@ " in
			*\ --add\ *)
				spk-add $pkg --count=$count
				count=$(($count + 1))
				continue ;;
		esac
		count=$(($count + 1))
		if [ "$output" = human ]; then
			[ "$count" = 1 ] && newline
			boldify $(gettext "Package") $pkg
			separator
			gettext "Status     :"; colorize 31 " not installed"
			gettext "Version    :"; echo "$mirrored" | awk -F'	' '{print " " $2}'
			gettext "Short desc :"; echo "$mirrored" | awk -F'	' '{print " " $4}'
			gettext "Category   :"; echo "$mirrored" | awk -F'	' '{print " " $3}'
			separator && newline
		else
			# Parse TSV fields (1=pkg 2=ver 3=cat 4=desc 5=web 8=deps 10=provide).
			m_ver=$(echo "$mirrored" | awk -F'	' '{print $2}')
			m_cat=$(echo "$mirrored" | awk -F'	' '{print $3}')
			m_desc=$(echo "$mirrored" | awk -F'	' '{print $4}')
			m_web=$(echo "$mirrored" | awk -F'	' '{print $5}')
			m_dep=$(echo "$mirrored" | awk -F'	' '{print $8}')
			m_prv=$(echo "$mirrored" | awk -F'	' '{print $10}')
			# db = $PKGS_DB for the official mirror, $extradb/<name>/ otherwise.
			m_src=mirror
			[ "$db" = "$PKGS_DB" ] || m_src=$(basename "$db")
			emit_info "$m_src" "$pkg" false "$m_ver" "$m_cat" \
				"$m_desc" "$m_web" "$m_dep" "$m_prv"
		fi
		continue
	fi
	unset mirrored
	# Skip options such as --confirm or unknown package
	case "$pkg" in
		--*) continue ;;
		*)
			if [ "$output" = human ]; then
				gettext "WARNING: Unknown package"; echo ": $pkg"
			elif [ "$output" = trame ]; then
				emit_info none "$pkg" false "" "" "" "" "" ""
			fi
			;;
	esac
done
if [ "$output" = trame ]; then
	printf '],"count":%s}\n' "$count"
fi
exit 0
