#!/bin/sh
#
# TazPkg - Tiny autonomous zone packages manager.
#
# This is a lightweight packages manager for *.tazpkg files written in SHell
# script. It works well with Busybox ash shell and bash. TazPkg lets you
# list, install, remove, download or get information about a package. You
# can use 'tazpkg usage' to get a list of commands with short descriptions.
# TazPkg also resolves dependencies and can upgrade packages from a mirror.
#
# (C) 2007-2015 SliTaz - GNU General Public License v3.
#
# Authors: See the AUTHORS files
#


####################
# Script variables #
####################

. /etc/slitaz/slitaz.conf
. /etc/slitaz/tazpkg.conf

. /lib/libtaz.sh
. /usr/lib/slitaz/libpkg.sh
. @@MODULES@@/find-depends

# Internationalization.
export TEXTDOMAIN='tazpkg'
_()  { local T="$1"; shift; printf "$(gettext "$T")" "$@"; echo; }
_n() { local T="$1"; shift; printf "$(gettext "$T")" "$@"; }
_p() {
	local S="$1" P="$2" N="$3"; shift 3;
	printf "$(ngettext "$S" "$P" "$N")" "$@"; }




# Remove all --parameters from cmdline
#-------------------------------------
# (thanks to libtaz.sh all --parameters are already set to variables)

IFS=$'\n'
set -- $(echo "$*" | sed '/^--/d')
unset IFS


# Initialize some variables to use words rather than numbers for functions
# and actions.

COMMAND="$1"
PACKAGE="${2%/}"
PACKAGE_DIR="$(cd $(dirname "$PACKAGE" 2>/dev/null) 2>/dev/null; pwd)"
[ -n "$PACKAGE" ] && PACKAGE_FILE="$PACKAGE_DIR/${PACKAGE##*/}"
if [ -f "$PACKAGE" ]; then
	# Set pkg basename for install, extract
	PACKAGE="$(basename "$PACKAGE" .tazpkg 2>/dev/null)"
else
	# Pkg name for remove, search and all other cmds
	PACKAGE="${PACKAGE%.tazpkg}"
fi
TARGET_DIR="$3"
export TOP_DIR="$(pwd)"
TMP_DIR="/tmp/$RANDOM"
INSTALL_LIST=''
SAVE_CACHE_DIR="$CACHE_DIR"

# Path to tazpkg used dir and configuration files
MIRROR="$PKGS_DB/mirror"
BLOCKED="$PKGS_DB/blocked-packages.list"
UP_LIST="$PKGS_DB/packages.up"
DEFAULT_MIRROR="$ONLINE_PKGS"

# TazPkg version
export VERSION=$(awk -F$'\t' '$1=="tazpkg"{print $2}' "$PKGS_DB/installed.info")
# User Agent
export UA="TazPkg-$VERSION"




####################
# Script functions #
####################


# Interactive mode

im() { tty -s; }


# Show debug messages
debug() {
	if [ -n "$debug" ]; then
		colorize 036 "$@" >&2
		echo -e "$(date +%f) $@" >> "${LOG/.log/.debug}"
	fi
}


# Check if dir exists

check_dir() {
	if [ ! -d "$1" ]; then
		action 'Creating folder "%s"...' "$1"
		mkdir -p "$1"
		status
		return 1
	fi
}


# Check if the directories and files used by TazPkg exist.
# If not and user is root we create them.

check_base_dir() {
	if [ "$(id -u)" = '0' ]; then
		check_dir $1$CACHE_DIR
		check_dir $1$INSTALLED
		check_dir $1$SLITAZ_LOGS
		if [ ! -f "$1$PKGS_DB/mirror" ]; then
			echo "${DEFAULT_MIRROR%/}/" > $1$PKGS_DB/mirror
			[ -n "$1" ] && cp $PKGS_DB/packages.* $1$PKGS_DB/
		fi
	fi
}
check_base_dir




# Check commandline for tazpkg syntax

check_cmd() {
	for i in $@; do
		case $i in
			su)
				check_root "$COMMAND"; continue;;
			pkg)
				[ -n "$PACKAGE" -o -n "$list" ] && continue
				newline; _ 'Please specify a package name on the command line.';;
			list)
				[ -n "$PACKAGE" ] && continue
				newline; _ 'Please specify a list name on the command line.';;
			flavor)
				[ -n "$PACKAGE" ] && continue
				newline; _ 'Please specify a flavor name on the command line.';;
			release)
				[ -n "$PACKAGE" ] && continue
				newline; _ 'Please specify a release name on the command line.';;
			file)
				[ -f "$PACKAGE_FILE" ] && continue
				newline; _ 'Unable to find file "%s"' "$PACKAGE_FILE";;
			dir)
				[ -d "$TARGET_DIR" ] && continue
				newline; _ 'Please specify an existing folder name on the command line.';;
			pattern)
				[ -n "$PACKAGE" ] && continue
				newline; _ 'Please specify a pattern to search for.';;
			receipt)
				[ -f "$INSTALLED/$PACKAGE/receipt" ] && continue
				newline; _ 'Unable to find the receipt "%s"' "$INSTALLED/$PACKAGE/receipt";;
		esac
		tazpkg -h "$COMMAND"
		exit 1
	done
}


# List support for getting/installing packages listed in the file
process_list() {
	debug "\nprocess_list()\n  list='$list'"
	local tmp_list pkg

	[ -z "$list" ] && return

	tmp_list="$(mktemp)"
	cp "$list" "$tmp_list"
	debug "  tmp_list='$tmp_list'"

	# Upgrade tazpkg first. It may handle new features/formats...
	# then upgrade essential packages early
	debug '  process important packages...'
	for pkg in glibc-base gcc-lib-base busybox-pam busybox slitaz-base-files tazpkg; do
		debug "  pkg='$pkg'"
		pkg=$(grep "^$pkg" "$tmp_list")
		[ -z "$pkg" ] && continue
		# Specify here empty list to prevent looping while recursive calls
		debug "  tazpkg $COMMAND '$pkg' --list=''"
		tazpkg $COMMAND "$pkg" --list=''
		sed -i "/^$pkg$/d" "$tmp_list"
	done

	# Process the rest of the list
	debug '  process the rest...'
	for pkg in $(cat "$tmp_list"); do
		debug "tazpkg $COMMAND '$pkg' --list=''"
		tazpkg $COMMAND "$pkg" --list=''
	done

	# Clean
	rm "$tmp_list"
	debug '  END: process_list()'
}




# Shared functions
# ----------------

# Log TazPkg activity

log_pkg() {
	local extra

	[ "$1" = 'Installed' ] && \
	extra=" - $(fgrep " $PACKAGE-$VERSION" $PKGS_DB/installed.$SUM | awk '{ print $1 }')"

	[ -e "$LOG" ] || touch $LOG

	[ -w "$LOG" ] &&
	echo "$(date +'%F %T') - $1 - $PACKAGE ($VERSION$EXTRAVERSION)$extra" >> $LOG
}


# Extract a package with cpio and gzip/lzma.

extract_package() {
	action 'Extracting package...'
	cpio -idm --quiet < "${PACKAGE_FILE##*/}" && rm -f "${PACKAGE_FILE##*/}"
	if [ -f fs.cpio.lzma ]; then
		unlzma < fs.cpio.lzma | cpio -idm --quiet && rm fs.cpio.lzma
	elif [ -f fs.cpio.gz ]; then
		zcat fs.cpio.gz | cpio -idm --quiet && rm fs.cpio.gz
	fi
	status
}


# Translate category names (must be last in line)

translate_category() {
	sed "s|base-system$|$(_ base-system)|g; s|x-window$|$(_ x-window)|g;
		s|utilities$|$(_ utilities)|g; s|network$|$(_ network)|g;
		s|graphics$|$(_ graphics)|g; s|multimedia$|$(_ multimedia)|g;
		s|office$|$(_ office)|g; s|development$|$(_ development)|g;
		s|system-tools$|$(_ system-tools)|g; s|security$|$(_ security)|g;
		s|games$|$(_ games)|g; s|misc$|$(_ misc)|g; s|meta$|$(_ meta)|g;
		s|non-free$|$(_ non-free)|g"
}


# If category is not one of those translated in native language, keep it
# untranslated. This allows both native and English language support.
# This also supports custom categories.
# And now we support spaces in translated categories

reverse_translate_category() {
	echo "$cat_i18n" | awk "BEGIN{FS=\"	\"}{if (/^$@	/) a=\$2}END{if (a==\"\") a=\"$@\"; print a}"
}

# END: Shared functions




###################
# TazPkg commands #
###################

case "$COMMAND" in
	call)
		# Call internal tazpkg function from external tazpkg module or other script.
		# Useful for functions sharing.
		shift
		# Check to call only tazpkg functions
		fgrep -q "$1()" "$0" && $@
		;;


	list|-l)
		# Various lists
		shift
		case $1 in
			b|blocked)			@@MODULES@@/list blocked;;
			c|cat|categories)	@@MODULES@@/list categories;;
			l|linked)			@@MODULES@@/list linked;;
			'')					@@MODULES@@/list installed;;
			*)					@@MODULES@@/list installed_of_category "$@";;
		esac
		;;
	-lb)	@@MODULES@@/list blocked;;
	-lc)	@@MODULES@@/list categories;;
	-ll)	@@MODULES@@/list linked;;

	-lm|list-mirror)	@@MODULES@@/list mirrored;;
	-lf|list-files)		check_cmd pkg; @@MODULES@@/list installed_files "$PACKAGE";;
	-a|activity|log)	@@MODULES@@/list activity;;
	list-config)		@@MODULES@@/list config_files "$2";;
	list-suggested)		@@MODULES@@/list suggested;;


	# Information about package
	info)
		check_cmd pkg; @@MODULES@@/info "$2";;


	desc|-d)
		# Display package description
		check_cmd pkg; @@MODULES@@/description "$2";;


	search|-s|-si|-sl)
		# Search for a package by pattern or name.
		check_cmd pattern

		# Extend short options to long analogs
		for i in $@; do
			case "$i" in
				-i|-si) export installed='yes';;
				-l|-sl) export list='yes';;
				-m)     export mirror='yes';;
			esac
		done

		@@MODULES@@/search pkg "$2"
		;;


	search-file|-sf)
		# Search for a file by pattern or name in all files.list.
		check_cmd pattern; @@MODULES@@/search file "$2";;


	search-pkgname|-sp)
		# Search for a package name
		check_cmd pattern; @@MODULES@@/search file2 "$2";;


	add-flavor)
		# Install a set of packages from a flavor.
		check_cmd su flavor; shift; @@MODULES@@/flavor $@;;
	install-flavor)
		# Install a set of packages from a flavor and purge other ones.
		check_cmd su flavor; shift; purge='yes' @@MODULES@@/flavor $@;;


	set-release)
		# Change current release and upgrade packages.
		check_cmd su release
		@@MODULES@@/set-release "$2";;


	remove|-r)
		# Remove packages.
		check_cmd su pkg; shift; @@MODULES@@/remove $@;;
		# TODO: remove multiple packages


	extract|-e)
		# Extract .tazpkg cpio archive into a directory.
		check_cmd pkg file; shift; @@MODULES@@/extract $@;;


	recompress)
		# Recompress .tazpkg cpio archive with lzma.
		check_cmd su pkg file; @@MODULES@@/recompress "$PACKAGE_FILE";;


	repack-config)
		# Create SliTaz package archive from configuration files.
		check_cmd su; @@MODULES@@/repack-config;;


	repack)
		# Create SliTaz package archive from an installed package.
		check_cmd pkg receipt; shift; @@MODULES@@/repack $@;;


	pack)
		# Create SliTaz package archive using cpio and lzma.
		# TODO: Cook also packs packages, we should share code in libpkg.sh
		check_cmd pkg; shift; @@MODULES@@/pack $@;;


	recharge)
		# Recharge packages databases from a mirror.
		#
		# WARNING: The 'mirrors' file has all SliTaz mirrors but 'mirror'
		# must have only the chosen main mirror.
		#
		check_cmd su; shift; @@MODULES@@/recharge $@;;


	up|upgrade)
		# This is the new way to upgrade packages making 'upgrade' and
		# upgradeable out-of-date. This new way is much, much more faster!
		# Look into installed packages and get data from receipt, it is fast
		# and easy to handle vars after using only md5sum to compare packages
		#
		check_cmd su
		for opt in $@; do
			case "$opt" in
				-i) export install='yes';;
				-c) export check='yes';;
			esac
		done

		@@MODULES@@/upgrade
		;;


	bugs)
		# Show known bugs in package(s)
		shift; @@MODULES@@/bugs $@;;


	check)
		# Check installed packages set.
		shift; @@MODULES@@/check $@;;


	block|-b|unblock|-u|chblock)
		# Add/remove a pkg name to the list of blocked packages.
		check_cmd su pkg; @@MODULES@@/block $@;;


	get|-g)
		# Download a package with wget.
		check_cmd pkg; shift
		# Get all the packages given on command line
		export nocache='yes'
		for i in $@; do
			pkg="$(@@MODULES@@/get $i)" && _ 'Done: %s' "${pkg##*/}"
		done
		# Get all the packages listed in the file
		process_list
		;;


	install|-i)
		# Install .tazpkg packages.
		check_cmd su pkg file; shift
		for i in $@; do
			@@MODULES@@/install $i
		done
		# Install all the packages listed in the file
		process_list
		;;


	get-install|-gi)
		# Download and install a package.
		check_cmd su pkg; shift
		export tazpkg_command='get-install'
		# Get and install all the packages given on command line
		for i in $@; do
			pkg="$(@@MODULES@@/get $i)" && @@MODULES@@/install "$pkg"
		done
		# Get and install all the packages listed in the file
		process_list
		;;


	get-list|install-list|get-install-list)
		# Install a set of packages from a list.
		check_cmd su list file
		COMMAND=${COMMAND%-list}
		export list="$2"
		process_list
		;;


	clean-cache|-cc)
		# Remove all downloaded packages.
		check_cmd su; @@MODULES@@/cache clean;;
	list-cache)
		# List of packages in the cache.
		@@MODULES@@/cache list;;


	list-undigest)
		# List undigest mirrors
		@@MODULES@@/mirror list;;
	remove-undigest)
		# Remove undigest mirror
		check_cmd su; shift; @@MODULES@@/mirror remove $@;;
	add-undigest|setup-undigest)
		# Add undigest mirror
		check_cmd su; shift; @@MODULES@@/mirror add $@;;
	setup-mirror|-sm)
		# Change main mirror
		check_cmd su; shift; @@MODULES@@/mirror setup $@;;


	reconfigure)
		# Replay post_install from receipt
		check_cmd su pkg receipt; @@MODULES@@/reconfigure "$2";;


	shell)
		# TazPkg SHell
		if [ "$(id -u)" -eq 0 ]; then
			PROMPT="\\033[1;33mtazpkg\\033[0;39m# "
		else
			PROMPT="\\033[1;33mtazpkg\\033[0;39m> "
		fi
		if [ -z "$noheader" ]; then
			clear
			title 'TazPkg SHell.'
			_ "Type 'usage' to list all available commands or 'quit' or 'q' to exit."
			newline
		fi
		while true; do
			echo -en "$PROMPT"; read cmd
			case $cmd in
				q|quit)
					break ;;
				shell)
					_ 'You are already running a TazPkg SHell.' ;;
				su)
					su -c 'exec tazpkg shell --noheader' && break ;;
				"")
					continue ;;
				*)
					tazpkg $cmd ;;
			esac
		done ;;


	depends)
		# Display dependencies tree
		check_cmd pkg; shift; @@MODULES@@/depends depends $@;;
	rdepends)
		# Display reverse dependencies tree
		check_cmd pkg; shift; @@MODULES@@/depends rdepends $@;;


	convert|-c)
		# convert misc package format to .tazpkg
		check_cmd file; shift; @@MODULES@@/convert $@;;


	link)
		# link a package from another SliTaz installation
		check_cmd su pkg dir; shift; @@MODULES@@/link $@;;


	help|-h)
		# TazPkg help system
		shift; @@MODULES@@/help $@
		;;


	mkdb)
		# Make TazPkg database
		shift; @@MODULES@@/mkdb $@
		;;


	'')
		# Default to summary
		@@MODULES@@/summary
		;;


	usage|*)
		# Print a short help or give usage for an unknown or empty command.
		@@MODULES@@/help
		;;
esac

exit 0
