#!/usr/bin/env sh

# Description: Extract and create archives
#
# Supported creation:
#   gz, bz2, xz, tar (with gz, bz2, xz), zip, 7z
#
# Supported extraction:
#    ar, arj, bz2, cab, chm, cpio, cramfs, deb, dmg, gz, iso, lzh, lzma,
#    msi, nsis, qcow2, rpm, squashfs, tar (with gz, bz2, xz), wim, xar,
#    xz, zip, Z, 7z
#
# Dependencies:
#   - tar, gzip, bzip2, xz
#   - 7zip
#
# Usage:
#   Extracting archives:
#     You can select multiple archives for batch extraction or content preview.
#
#   Creating archives:
#     Enter a filename with extension (e.g., foo.tar.gz, foo.txz, foo.zip) to
#     archive and compress selected files together.
#
#     For tar-based formats:
#       Files will be archived using relative paths only if all selected files
#       are located within the current directory or its subdirectories;
#       otherwise, absolute paths will be used.
#
#     For zip or 7z formats:
#       The directory hierarchy of the selected files will be ignored, and all
#       files will be packed directly into the archive without preserving paths.
#
#     If you enter just .gz, .bz2, or .xz as the filename, each selected file will
#     be individually compressed in the specified format.
#
# Shell: POSIX compliant
#
# Author: Shi Yanling

sffpipe=$1
sffdir=${sffpipe%/*}
tmpdir=${TMPDIR:-/tmp}
[ ! -w "$tmpdir" ] && tmpdir=$sffdir
uid=$(id -u)
tsel="${tmpdir}/sff-tmpsel-$uid"
tbuf1="${tmpdir}/sff-tmpbuf2-$uid"
type 7zz >/dev/null 2>&1 && _7z='7zz' || _7z='7z'

sffpipe_refresh()
{
	[ "$1" = '-c' ] && _x='.' || _x=''
	printf "*%s" "$_x" >"$sffpipe"
}

sffpipe_sel_file()
{
	printf "@%s\0" "$1" >"$sffpipe"
}

sffpipe_get_sel()
{
	printf "%s" "$$" >"$sffpipe"
	sel=$sffpipe
}

extract_archive()
{
	sffpipe_get_sel
	tr '\n\0' '\035\n' <"$sel" >"$tsel"
	[ ! -s "$tsel" ] && return 0

	printf "\n(l)ist contents / (e)xtract / (c)ancel [c]: "; read -r _x

	_selfile=''
	while IFS='' read -r _path <&3; do
		_path=$(printf "%s" "$_path" | tr '\035' '\n')
		_wdir=${_path%/*}
		_name=${_path##*/}
		_bname=${_name%.?*}
		_bname=${_bname%.tar}

		case "$_name" in
		*?.?*) [ -d "$_path" ] && continue;;
		*) continue;;
		esac

		case "$_x" in
		'e') if [ -e "${_wdir}/$_bname" ]; then
				printf "\n'%s' exists. Overwrite? (y/n) [n]: " "$_bname"; read -r _x2
				[ "$_x2" != 'y' ] && continue
			fi
	 		_tdir=$(mktemp -d "${_wdir}/tmp.XXXXXX")
			[ -e "${_wdir}/$_bname" ] && rm -rf "${_wdir}/$_bname"

			echo "Extracting..."
			case "$(printf "%s" "$_name" | tr 'A-Z' 'a-z')" in
			*?.tar.?*|*?.t?*) cd "$_tdir" && tar -xvf "$_path";;
			*?.gz) gunzip -k "$_path";;
			*?.bz2) bunzip2 -k "$_path";;
			*?.xz) unxz -k "$_path";;
			*?.?*) cd "$_tdir" && "$_7z" x -aoa "$_path";;
			esac && _selfile="$_bname" || { printf "Press Enter to continue "; read -r _x2; }

			_num=$(find "$_tdir"/ -mindepth 1 -maxdepth 1 ! -name . ! -name .. -print0 | tr '\n\0' '\035\n' | wc -l)
			if [ -d "${_tdir}/$_bname" ] && [ "$_num" -eq 1 ]; then
				mv "${_tdir}/$_bname" "$_wdir"/
			elif [ ! -e "${_wdir}/$_bname" ] && [ "$_num" -gt 0 ]; then
				mv "$_tdir" "${_wdir}/$_bname"
			fi
			[ -e "$_tdir" ] && rm -rf "$_tdir"
			;;

		'l') echo ""
			case "$(printf "%s" "$_name" | tr 'A-Z' 'a-z')" in
			*?.tar.?*|*?.t?*) tar -tvf "$_path";;
			*?.gz) zcat "$_path";;
			*?.bz2) bzcat "$_path";;
			*?.xz) xzcat "$_path";;
			*?.?*) "$_7z" l "$_path";;
			esac
			printf "Press Enter to continue "; read -r _x2
			;;
		*) return 0
			;;
		esac
	done 3<"$tsel"

	if [ "$_selfile" ]; then
		sffpipe_sel_file "$_selfile"
	else
		sffpipe_refresh
	fi
}

create_archive()
{
	sffpipe_get_sel
	tr '\n\0' '\035\n' <"$sel" >"$tsel"
	[ ! -s "$tsel" ] && return 0

	while true; do
		printf "\nArchive name (empty to cancel): "; read -r _name
		[ -z "$_name" ] && return 0
		if [ -e "$_name" ]; then
			printf "\n'%s' exists. Overwrite? (y/n) [n]: " "$_name"; read -r _x
			[ "$_x" = 'y' ] && break
		else
			break
		fi
	done

	_pwd=$(printf '%s' "$PWD" | tr '\n' '\035' | sed -e 's/[^^]/[&]/g' -e 's/\^/\\^/g')
	if [ "$(sed "/^$_pwd\//d" "$tsel")" ]; then
		tr '\n\035' '\0\n' <"$tsel" >"$tbuf1"
	else
		sed "s|^$_pwd/||" "$tsel" | tr '\n\035' '\0\n' >"$tbuf1"
	fi
	[ -e "$_name" ] && rm -f "$_name"

	case "$(printf "%s" "$_name" | tr 'A-Z' 'a-z')" in
	*?.tar.?*|*?.t?*) xargs -0 tar -cavf "$_name" -- <"$tbuf1";;
	*.gz) xargs -0 gzip -fk -- <"$tbuf1";;
	*.bz2) xargs -0 bzip2 -fk -- <"$tbuf1";;
	*.xz) xargs -0 xz -fk -- <"$tbuf1";;
	*?.zip|*?.7z) xargs -0 "$_7z" a "$_name" -- <"$tbuf1";;
	*) printf "\nUnsupported archive format\n"; false;;
	esac

	if [ "$?" -eq 0 ]; then
		 sffpipe_sel_file "$_name"
	else
		sffpipe_refresh
		printf "Press Enter to continue "; read -r _x
	fi
}

case "$2" in
'e') extract_archive;;
'c') create_archive;;
esac
rm -f "$tsel" "$tbuf1"
