summaryrefslogblamecommitdiffstats
path: root/experiment/tar-wrapper
blob: 0730203d087f035a7c7f30936558832d9a905ad4 (plain) (tree)






















































































































































































































                                                                                       
#! /bin/bash

# Recognizes tar archive extraction using the options listed below.  Any other
# operations or options will cause the tar command to be executed directly,
# with no special processing.

declare -r short_opts=ajJvxzZ
declare -r short_opts_arg=CfI
declare -r -A long_opts=(
	[--auto-compress]=1 [--bzip2]=1 [--compress]=1 [--extract]=1 [--file]=1
	[--get]=1 [--gunzip]=1 [--gzip]=1 [--lzip]=1 [--lzma]=1 [--lzop]=1
	[--no-auto-compress]=1 [--uncompress]=1 [--ungzip]=1 [--verbose]=1
	[--xz]=1 [--zstd]=1
)
declare -r -A long_opts_arg=(
	[--use-compress-program]=1
)

command=${0##*/}
original_args=("$@")
args=()

opt_extract=
opt_directory=
opt_file=

PATH=${PATH#*:}

traditional_to_unix() {
	local opts=$1 args=()
	shift

	[[ "$opts" =~ ^[Acdrtux] ]] || return

	while [ -n "$opts" ]; do
		args+=("-${opts:0:1}")
		if [[ "$opts" =~ ^[$short_opts_arg] ]]; then
			[ $# -ge 1 ] || return 1
			args+=("$1")
			shift
		else
			[[ "$opts" =~ ^[$short_opts] ]] || return
		fi
		opts="${opts:1}"
	done

	printf "%q " "${args[@]}" "$@"
}

debug() {
	echo "$@">&2
}

fallback() {
	debug "falling back to $command"
	exec "$command" "${original_args[@]}"
}

[ $# -gt 0 ] || fallback

if ! [[ "$1" =~ ^- ]]; then
	args=$(traditional_to_unix "$@") || fallback
	set -- $args
fi

set_opt_file() {
	[ -n "$1" ] || fallback
	opt_file=$1
}

set_opt_directory() {
	[ -n "$1" ] || fallback
	opt_directory=$1
}

short_opt() {
	debug option "$1"
	case "$1" in
	-x)
		opt_extract=1
		;;
	esac
	args+=("$1")
}

short_opt_arg() {
	debug option "$1" "$2"
	case "$1" in
	-C)
		set_opt_directory "$2"
		;;
	-f)
		set_opt_file "$2"
		;;
	*)
		args+=("$1$2")
		;;
	esac
}

long_opt() {
	debug option "$1" "$2"
	case "$1" in
	--extract|--get)
		opt_extract=1
		;;
	esac
	args+=("$1")
}

long_opt_arg() {
	debug option "$1" "$2"
	case "$1" in
	--directory)
		set_opt_directory "$2"
		;;
	--file)
		set_opt_file "$2"
		;;
	*)
		args+=("$1=$2")
		;;
	esac
}

while [ $# -gt 0 ]; do
	if [[ "$1" =~ ^-- ]]; then
		[ "$1" != -- ] || break
		if [[ "$1" =~ ^(--.*)=(.*) ]]; then
			[ -n "${long_opts_arg[${BASH_REMATCH[1]}]}" ] || fallback
			long_opt_arg "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
			shift
		elif [ -n "${long_opts_arg[$1]}" ]; then
			[ $# -ge 2 ] || fallback
			long_opt_arg "$1" "$2"
			shift 2
		elif [ -n "${long_opts[$1]}" ]; then
			long_opt "$1"
			shift
		else
			fallback
		fi
	elif [[ "$1" =~ ^- ]]; then
		[ "$1" != - ] || break
		if [[ "$1" =~ ^(-[$short_opts])(.*) ]]; then
			short_opt "${BASH_REMATCH[1]}"
			shift
			if [ -n "${BASH_REMATCH[2]}" ]; then
				set -- "-${BASH_REMATCH[2]}" "$@"
			fi
		elif [[ "$1" =~ ^(-[$short_opts_arg])(.*) ]]; then
			if [ -z "${BASH_REMATCH[2]}" ]; then
				[ $# -ge 2 ] || fallback
				short_opt_arg "${BASH_REMATCH[1]}" "$2"
				shift 2
			else
				short_opt_arg "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
				shift
			fi
		else
			fallback
		fi
	else
		break
	fi
done

[ -n "$opt_extract" ] || fallback
! [[ "/${opt_directory:-.}/" =~ /\.\./|^// ]] || fallback

mkpath() {
	if [ "$1" == . ]; then
		echo "$2"
	elif [ "$2" == . ]; then
		echo "$1"
	else
		echo "$1/$2"
	fi
}

if [ -n "$QUILT_WORKDIR" ]; then
	cleanup() {
		rm -rf "$tmpdir"
	}

	dir=${opt_directory:-.}
	pattern=$(mkpath "$dir" ".$command.XXXXXX")
	tmpdir=$(mktemp -d "$pattern" 2> /dev/null) || fallback
	trap cleanup EXIT
	opt_directory=$tmpdir
fi

[ -z "$opt_directory" ] || args+=("-C$opt_directory")
[ -z "$opt_file" ] || args+=("-f$opt_file")

args+=("$@")

"$command" "${args[@]}" || exit

if [ -n "$QUILT_WORKDIR" ]; then
	workdir=$(mkpath "$QUILT_WORKDIR" "$dir")
	shopt -s dotglob
	files=( "$opt_directory"/* )
	for file in "${files[@]##*/}"; do
		if [ -e "$(mkpath "$dir" "$file")" -o \
		     -e "$(mkpath "$workdir" "$file")" ]; then
			printf $"Refusing to overwrite %s\n" \
				"$(mkpath "$dir" "$file")"
			exit 1
		fi
	done
	mkdir -p "$workdir" || exit
	cp -dlR "${files[@]}" "$workdir/" || exit
	mv "${files[@]}" "$dir/" || exit
fi