#!/bin/bash
#
# Copyright (C) 2013, 2023  Etersoft
# Copyright (C) 2013, 2023  Vitaly Lipatov <lav@etersoft.ru>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

have_patool()
{
	# TODO: optimize?
	[ -n "$use_7z" ] && return 1
	[ -n "$use_patool" ] || is_command patool
}

set_backend()
{
	HAVE_7Z='7z'

	[ "$ERC_BACKEND" = "patool" ] && use_patool=1
	[ "$ERC_BACKEND" = "7z" ] && use_7z=1

	[ -n "$use_patool" ] && [ -n "$use_7z" ] && fatal "Don't use --use-7z and --use-patool simulateously."

	if [ -n "$use_7z" ] || ! have_patool ; then
		# TODO: try install patool and p7zip
		if ! is_command $HAVE_7Z ; then
			# 7-zip
			is_command 7zz && HAVE_7Z='7zz'
		fi
		if ! is_command $HAVE_7Z ; then
			# reduced p7-zip
			is_command 7zr && HAVE_7Z='7zr'
		fi
		if ! is_command $HAVE_7Z ; then
			# 7-zip Standalone/Alternative
			is_command 7za && HAVE_7Z='7za'
		fi
		is_command $HAVE_7Z || fatal "Could not find any patool or 7-zip backend. Please install p7zip package and retry."
	fi
	[ -z "$force" ] || HAVE_7Z="$HAVE_7Z -y"
}


list_subformats()
{
	local i
	for i in gz bz2 xz bzip2 lz4 lzma zst zstd ; do
		echo "tar.$i"
	done

	for i in gz bz2 xz lz4 ; do
		echo "cpio.$i"
	done
}

list_extraformats()
{
	cat <<EOF
bz2
gz
lz4
Z
7Z
tgz
pax
zst
zstd
exe
dll
AppImage
appimage
squashfs
snap
EOF
}

# it is extension list really
# list all supported formats: tar rar and so on
list_formats()
{
	list_subformats
	if have_patool ; then
		a='' patool formats | grep ".* files:" | sed "s/ .*//g"
	else
		for i in tar zip 7z 7Z tar.7z tar.7Z ; do
			echo "$i"
		done
		list_subformats
	fi
	# TODO: do not use patool formats in such case?
	# See ArchiveCompressions in patool
	list_extraformats
}

# returns true if arg is XXX: from list_formats
is_target_format()
{
	[ "${1/:/}" = "$1" ] && return 1
	local arc="$(echo "$1" | sed -e 's|:.*||g')"
	# Security: use -Fx for exact string match, not regex
	list_formats | grep -qFx "$arc" && return 0
	return 1
}

# TODO: detect by name, without comparing with existing?
# 1.zip -> zip
get_archive_ext()
{
	local i
	for i in $(list_formats) ; do
		[ -z "${1/*.$i/}" ] && echo "$i" && return
	done
	return 1
}

# 1.zip -> 1
get_archive_name()
{
	local ext
	ext=$(get_archive_ext "$1")
	if [ -n "$ext" ] ; then
		basename "$1" .$ext
	else
		echo "$1"
		return 1
	fi
}


# Detect archive type: try extension first, then content detection via file(1)
get_archive_type()
{
	local type
	if type=$(get_archive_ext "$1") ; then
		echo "$type"
		return
	fi

	if type=$(detect_by_content "$1") && [ "$type" != "plain" ] ; then
		echo "$type"
		return
	fi
	return 1
}

extract_command() {
    local cmd="$1"
    local archive="$2"

    if is_command tar; then
        docmd $cmd "$archive"
    else
        docmd $HAVE_7Z x -so "$archive" | docmd $HAVE_7Z x -y -si -ttar
    fi
}

# Read up to 14 bytes as hex string (from file or stdin)
bytes_to_hex()
{
    od -A n -t x1 -N 14 "$@" 2>/dev/null | tr -d ' \n'
}

# Convert hex string to raw bytes
hex_to_bytes()
{
    printf '%b' "$(echo "$1" | sed 's/../\\x&/g')"
}

# Convert MIME type to short format name
# application/x-bzip2 -> bzip2, text/* -> plain, etc.
# Returns 1 for unrecognized MIME types
mime_to_type()
{
    case "$1" in
        text/*) echo "plain" ; return ;;
    esac
    local type="${1#application/}"
    [ "$type" != "$1" ] || return 1
    case "$type" in
        x-7z-compressed) type="7z" ;;
        vnd.rar)         type="rar" ;;
        *)               type="${type#x-}" ;;
    esac
    case "$type" in
        gzip|bzip2|xz|lz4|zstd|lzma|compress) ;;
        zip|tar|7z|rar) ;;
        *) return 1 ;;
    esac
    echo "$type"
}

# Detect compression format by file content via file(1)
detect_by_content()
{
    local file="$1"
    [ -r "$file" ] || return 1
    mime_to_type "$(file --mime-type -b "$file" 2>/dev/null)"
}
