diff options
Diffstat (limited to 'scripts/wzpaq')
-rwxr-xr-x | scripts/wzpaq | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/scripts/wzpaq b/scripts/wzpaq new file mode 100755 index 0000000..0f9b6eb --- /dev/null +++ b/scripts/wzpaq @@ -0,0 +1,294 @@ +#!/bin/sh + +# Wrap zpaq as a more generic compression utility +# +# Copyright 2017 Robert Krawitz (rlk@alum.mit.edu) +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2 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 General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +declare -r ZPAQ=/usr/bin/zpaq +declare METHOD=5 +declare VERBOSE= +declare ZPCAT= +declare DECOMPRESS= +declare FORCE= +declare KEEP= +declare SAVE_META= +declare RECURSE= +declare STDIN= +declare STATUS=0 +declare -r PROGNAME=$0 +declare -r BASEDIR=$(pwd) +declare -r RM=rm +declare TMP_FILE= +declare TMP_DIR= +declare TMP_ARCHIVE= + +usage() { + declare -l USTATUS=${1:-0} + echo "Usage: $PROGNAME [OPTIONS] [FILE]..." + echo "Compress or decompress FILEs (by default, compress FILEs in place)" + echo + echo " -c Write to standard output, keep original files unchanged" + echo " -d Decompress" + echo " -f Force overwrite of output file" + echo " -h Print this help" + echo " -k Keep (don't delete) input files" + echo " -n Do not preserve original filename and timestamp" + echo " -N Preserve original filename and timestamp" + echo " -q Suppress all messages" + echo " -r Operate recursively on directories" + echo " -v Print verbose messages" + echo " -1 Use minimum compression" + echo " -5 Use maximum compression" + echo + echo "With no FILE, or when only FILE is -, read standard input" + exit $USTATUS +} + +while getopts "qvcdhfknNz0123456789" opt ; do + case "$opt" in + q) VERBOSE= ;; + v) VERBOSE=1 ;; + c) ZPCAT=1 ;; + d) DECOMPRESS=1 ;; + f) FORCE=1 ;; + h) usage ;; + k) KEEP=1 ;; + n) SAVE_META=-noattributes ;; + N) SAVE_META= ;; + r) RECURSE=1 ;; + z) DECOMPRESS= ;; + [0-9]) METHOD=$opt ;; + *) usage 1 ;; + esac +done +shift $(($OPTIND-1)) + +if [[ -n $RECURSE && -n $ZPCAT ]] ; then + echo "May not combine recursive and output to stdout" + usage $0 1 +fi + +if [[ -n $RECURSE && -z $* ]] ; then + echo "May not use recursive with no inputs" + usage $0 1 +fi + +declare -a FILES + +g() { + if [[ $1 == /* ]] ; then + echo "$1" + else + echo "$BASEDIR/$1" + fi +} + +build_file_list() { + for f in "$@" ; do + if [[ -d $f ]] ; then + if [[ -n $RECURSE ]] ; then + OIFS=$IFS + IFS= + if [[ -n $DECOMPRESS ]] ; then + for f in $(find "$f" -type f -name '*.zpaq' -print) ; do + FILES+=($(g "$f")) + done + else + for f in $(find "$f" -type f \! -name '*.zpaq' -print) ; do + FILES+=($(g "$f")) + done + fi + IFS=$OIFS + else + echo "$f: is a directory, skipping" 1>&2 + STATUS=1 + fi + elif [[ -f $f ]] ; then + if [[ -n $DECOMPRESS && $f != *.zpaq ]] ; then + echo "$f: not a zpaq archive, skipping" 1>&2 + STATUS=1 + elif [[ -z $DECOMPRESS && $f == *.zpaq ]] ; then + echo "$f: is a zpaq archive, skipping" 1>&2 + STATUS=1 + else + FILES+=($(g "$f")) + fi + else + echo "$f: not a plain file, skipping" 1>&2 + STATUS=1 + fi + done +} + +if [[ -z $* || $* == '-' ]] ; then + STDIN=1 + ZPCAT=1 +else + build_file_list "$@" +fi + +if [[ -n $ZPCAT && ${#FILES[@]} > 1 ]] ; then + echo "May not compress/decompress more than one file to stdout" 1>&2 + usage 1 +fi + +# Wrappers to simplify debugging + +run_zpaq() { + if [[ -n $VERBOSE ]] ; then + "$ZPAQ" "$@" + else + "$ZPAQ" "$@" >/dev/null 2>&1 + fi +} + +RM() { + for f in "$@" ; do + if [[ $1 != $TMP_FILE && $1 != $TMP_DIR || $1 != $TMP_ARCHIVE ]] ; then + rm -rf $f + fi + done +} + +do_decompress() { + FILE="$1" + DEST="$2" + [[ -n $3 && -d $3 ]] && cd $3 + run_zpaq extract "$FILE" $SAVE_META + [[ $? > 0 ]] && STATUS=1 + if [[ $(wc -l <<< "$(find . -type f -print)") > 1 ]] ; then + echo "Multiple file archive $TMP_ARCHIVE, skipping" 1>&2 + exit 1 + fi + if [[ -z $DEST ]] ; then + cat "$(find . -type f -print)" + else + mv "$(find . -type f -print)" "$DEST" + fi +} + +decompress_file() { + FILE="$1" + DEST="$2" + if [[ -d $TMP_DIR ]] ; then + # Decompress into a temporary directory. We don't actually + # know what's in the archive and don't want to dump arbitrary + # contents into the user's directory. We do this in a subshell + # so that we don't have to try to cd back to the current directory + # (which may be problematic if the directory is remote and we + # spend a lot of time in the temp directory). + (do_decompress "$FILE" "$DEST" "$TMP_DIR") + if [[ $? > 0 ]] ; then + STATUS=1 + elif [[ -z $ZPCAT && ! -n $KEEP ]] ; then + RM "$FILE" + fi + fi +} + +decompress() { + # Decompress: decompress all .zpaq files. + if [[ -z ${FILES[*]} ]] ; then + # Decompress stdin implies output to stdout + ZPCAT=1 + cat > "$TMP_ARCHIVE" + [[ $? == 0 ]] && decompress_file "$TMP_ARCHIVE" + else + for f in ${FILES[@]} ; do + declare DEST= + [[ -z $ZPCAT ]] && DEST="${f%.zpaq}" + if [[ -n $DEST && -e $DEST && -z $FORCE ]] ; then + echo "${DEST#${BASEDIR}/} exists, skipping" 1>&2 + STATUS=1 + else + decompress_file "$f" "$DEST" + fi + done + fi +} + +do_compress() { + FILE="$1" + DEST=$(g "$2") + cd "${FILE%/*}" + run_zpaq add "$DEST" "${FILE##*/}" $SAVE_META -method "$METHOD" +} + +compress_file() { + FILE="$1" + DEST=${2:-${FILE}.zpaq} + # Make sure that DEST really is empty. + RM "$DEST" + (do_compress "$FILE" "$DEST") + if [[ $? > 0 ]] ; then + return 1 + elif [[ -n "$ZPCAT" ]] ; then + # zpaq won't send anything to stdout; we have to + cat "$DEST" + RM "$DEST" + elif [[ -z $KEEP ]] ; then + RM "$FILE" + fi + return 0 +} + +compress() { + if [[ -z ${FILES[*]} ]] ; then + # Compress stdin implies output to stdout + ZPCAT=1 + cat > "$TMP_FILE" + if [[ $? == 0 ]] ; then + compress_file "$TMP_FILE" "$TMP_ARCHIVE" + [[ $? > 0 ]] && STATUS=1 + fi + else + for f in ${FILES[@]} ; do + declare DEST="${f}.zpaq" + [[ -n $ZPCAT ]] && DEST="$TMP_ARCHIVE" + if [[ -z $ZPCAT && -e $DEST && -z $FORCE ]] ; then + echo "${DEST#${BASEDIR}/} exists, skipping" 1>&2 + STATUS=1 + else + compress_file "$f" "$DEST" + [[ $? > 0 ]] && STATUS=1 + fi + done + fi +} + +# If all of the command line files were disqualified because they +# are compressed files, don't try to compress stdin. +[[ -n $* && -z ${FILES[*]} ]] && exit 1 + +cleanup() { + [[ -n $TMP_FILE && -f $TMP_FILE ]] && rm -f "$TMP_FILE" + [[ -n $TMP_ARCHIVE && -f $TMP_ARCHIVE ]] && rm -f "$TMP_ARCHIVE" + [[ -n $TMP_DIR && -d $TMP_DIR ]] && rm -rf "$TMP_DIR" +} + +trap cleanup EXIT + +declare TMP_FILE=$(mktemp /tmp/wzpaqinXXXXXXXXX) +declare TMP_DIR=$(mktemp -d /tmp/wzpaq_outXXXXXXXXX) +declare TMP_ARCHIVE=$(mktemp /tmp/wzpaqXXXXXXXXX.zpaq) + +if [[ -n $DECOMPRESS ]] ; then + decompress +else + compress +fi + +exit $STATUS |