#!/bin/bash
# This script is meant to recursively re-archive all the found archives in first
# argument to the script (a directory), in 7zip, to preserve HDD space.

# for zip in *.tar.xz; do dir="${zip%.tar.xz}"; mkdir -p "$dir" || break; cd "$dir"; arch_unpack.sh ../"$zip" || break; cd ..; done

set -o pipefail

c_tty=$(tty)

# trap ctrl-c and call ctrl_c()
trap ctrl_c INT

ctrl_c () {
	restore
	echo '** Trapped CTRL-C'
	exit
}

if [[ $(whoami) != 'root' ]]; then
	echo -e "You need to be root to run this script!\n"
	exit
fi

regex='((\.tar\.bz2)|(\.tbz)|(\.tbz2)|(\.tar\.xz)|(\.txz)|(\.tar\.gz)|(\.tgz)|(\.tar\.z))$'
regex2='((\.lzh)|(\.gz)|(\.z)|(\.bz2)|(\.xz)|(\.zip)|(\.rar)|(\.arj))$'

if [[ ! -d $1 ]]; then
	echo -e "Usage: $(basename "$0") [dirs...]\n"
	exit
fi

library=$(readlink -f "$1")
home_dir='/home/lucifer'
owner='lucifer:lucifer'
stderr_f="/dev/shm/arch_pack_stderr-${RANDOM}.txt"

restore () {
	exec 2>"$c_tty"
	cat_stderr
	rm -f "$stderr_f"
}

cat_stderr () {
	stderr_out=$(cat "$stderr_f")

	truncate -s 0 "$stderr_f"

	if [[ $stderr_out ]]; then
		echo "$stderr_out"
	fi
}

# Creates a function, called 'output', which will let the user know if
# the extraction went okay or not. If not, print the entire output from
# the compression program.
output () {
	print_stdout () {
		for (( n = 0; n < ${last}; n++ )); do
			echo "${stdout_v[${n}]}"
		done
		unset -v stdout_v
		cat_stderr
	}

	if [[ ${#stdout_v[@]} -gt 0 ]]; then
		last=$(( ${#stdout_v[@]} - 1 ))
	fi

	if [[ "${stdout_v[${last}]}" == "0" ]]; then
		echo -e "${f}: Everything is Ok\n"
#		print_stdout
	else
		echo -e "${f}: Something went wrong\n"
		print_stdout

		if [[ -d $newfn_tmp ]]; then
			rm -rf "$newfn_tmp"
		fi

		exit
	fi
}

# This function extracts archives and then lets you know if there were
# errors or not.
arch_unpack_func () {
	touch "$stderr_f"
	exec 2>>"$stderr_f"

	# If there are no arguments to the script, then exit.
	if [[ ! -f $1 ]]; then
		return
	fi

	while [[ $# -gt 0 ]]; do
		f=$(readlink -f "$1")
		f_bn=$(basename "$f")
		f_bn_lc=$(tr '[[:upper:]]' '[[:lower:]]' <<<"$f_bn")

		if [[ ! -f $f || ! -r $f ]]; then
			return
		fi

		case "$f_bn_lc" in
			*.tar.z|*.tar.gz|*.tgz)
				mapfile -t stdout_v < <(gunzip "$f"; echo "$?")
				output
			;;
			*.tar.bz2|*.tbz|*.tbz2)
				mapfile -t stdout_v < <(bunzip2 "$f"; echo "$?")
				output
			;;
			*.tar.xz|*.txz)
				mapfile -t stdout_v < <(unxz "$f"; echo "$?")
				output
			;;
			*.lzh)
				mapfile -t stdout_v < <(7z x "$f"; echo "$?")
				output
			;;
			*.z|*.gz)
				mapfile -t stdout_v < <(gunzip "$f"; echo "$?")
				output
			;;
			*.bz2)
				mapfile -t stdout_v < <(bunzip2 "$f"; echo "$?")
				output
			;;
			*.xz)
				mapfile -t stdout_v < <(unxz "$f"; echo "$?")
				output
			;;
			*.zip)
				mapfile -t stdout_v < <(unzip "$f"; echo "$?")
				output
			;;
			*.rar)
				mapfile -t stdout_v < <(${home_dir}/bin/rar x "$f"; echo "$?")
				output
			;;
			*.arj)
				mapfile -t stdout_v < <(7z x "$f"; echo "$?")
				output
			;;
		esac

		shift
	done

	restore
}

# This function compresses files / directories to archives (in chosen
# format) and then lets you know if there were errors or not. In all
# cases, the script uses maximum compression level.
arch_pack_func () {
# Creates a variable ($f) with the archive name, which is supposed to be
# the first argument to the script.
	f=$(readlink -f "$1")
	f_bn=$(basename "$f")

	touch "$stderr_f"
	exec 2>>"$stderr_f"

# Creates an array ($files) with all the files and directories to be
# compressed.
	files_tmp=("$@")

# Deletes the first element of the files_tmp array, because that's the
# intended name of the archive, and not a file to be compressed.
	unset -v files_tmp[0]
    
# If there are no arguments to the script, print usage and then exit.
	if [[ -z $1 || -z $2 ]]; then
		return
	fi

# Depending on the filename extension, create an archive accordingly.
	case "$f" in
		*.7z)
			mapfile -t stdout_v < <(7za a -t7z -m0=lzma -mx=9 -mfb=64 -md=32m -ms=on "$f" "${files_tmp[@]}"; echo "$?")
			output
		;;
	esac

	restore
}

mapfile -t files_all < <(find "$library" -type f -iname "*")

declare -a files

for (( i = 0; i < ${#files_all[@]}; i++ )); do
	fn="${files_all[${i}]}"
	bn_lc=$(basename "$fn" | tr '[:upper:]' '[:lower:]')

	if [[ $bn_lc =~ $regex ]]; then
		files+=("$fn")
	fi
done

for (( i = 0; i < ${#files[@]}; i++ )); do
#	sleep 1

	fn="${files[${i}]}"
	dn=$(dirname "$fn")
	bn=$(basename "$fn")
	bn_of=$(sed -E 's/(\.tar){0,1}\.[[:alnum:]]{1,3}$/\.tar/i' <<<"$bn")

	newfn_bn="${dn}/${bn_of}"
	newfn_tmp="${newfn_bn}-${RANDOM}"
	newfn="${newfn_tmp}/${bn_of}.7z"

	mkdir -p "$newfn_tmp"
	cp -p "$fn" "$newfn_tmp"
	cd "$newfn_tmp"

	arch_unpack_func "$bn"

	mapfile -t fn_tmp < <(find . -type f -iname "*" | sed 's/^\.\///')

	if [[ ${fn_tmp[0]} != $bn_of ]]; then
		mv -n "${fn_tmp[0]}" "$bn_of"
	fi

	arch_pack_func "${newfn}" "$bn_of"

	if [[ -f $fn ]]; then
		rm "$fn"
	fi

	if [[ ! -f "${newfn_bn}.7z" && ! -d "${newfn_bn}.7z" ]]; then
		newfn_of="${newfn_bn}.7z"
	else
		newfn_of="${newfn_bn}-${RANDOM}.7z"
	fi

	mv -n "$newfn" "$newfn_of"
	chown "$owner" "$newfn_of"

	if [[ -d $newfn_tmp ]]; then
		rm -rf "$newfn_tmp"
	fi
#echo "***"
#echo "fn: ${fn}"
#echo "newfn: ${newfn}"
#echo "newfn_tmp: ${newfn_tmp}"
done

unset -v files
declare -a files

mapfile -t files_all < <(find "$library" -type f -iname "*")

for (( i = 0; i < ${#files_all[@]}; i++ )); do
	fn="${files_all[${i}]}"
	bn_lc=$(basename "$fn" | tr '[:upper:]' '[:lower:]')

	if [[ $bn_lc =~ $regex2 ]]; then
		files+=("$fn")
	fi
done

for (( i = 0; i < ${#files[@]}; i++ )); do
#	sleep 1

	fn="${files[${i}]}"
	dn=$(dirname "$fn")
	bn=$(basename "$fn")
	bn_of=$(sed -E 's/\.[[:alnum:]]+$//' <<<"$bn")

	newfn_bn="${dn}/${bn_of}"
	newfn_tmp="${newfn_bn}-${RANDOM}"
	newfn="${newfn_tmp}/${bn_of}.7z"

	mkdir -p "$newfn_tmp"

	cp -p "$fn" "$newfn_tmp"

	cd "$newfn_tmp"
	arch_unpack_func "$bn"

	if [[ -f $bn ]]; then
		rm "$bn"
	fi

	arch_pack_func "${newfn}" *

	if [[ -f $fn ]]; then
		rm "$fn"
	fi

	if [[ ! -f "${newfn_bn}.7z" && ! -d "${newfn_bn}.7z" ]]; then
		newfn_of="${newfn_bn}.7z"
	else
		newfn_of="${newfn_bn}-${RANDOM}.7z"
	fi

	mv -n "$newfn" "$newfn_of"
	chown "$owner" "$newfn_of"

	if [[ -d $newfn_tmp ]]; then
		rm -rf "$newfn_tmp"
	fi
#echo "***"
#echo "fn: ${fn}"
#echo "newfn: ${newfn}"
#echo "newfn_tmp: ${newfn_tmp}"
done

#find "$library" -mindepth 1 -type d -empty | while read dn; do
#	true
#	rm -rf "$dn"
#echo "empty: ${dn}"
#done
