From 5284ab29e23d08d11a2a2d003228b4299e539278 Mon Sep 17 00:00:00 2001 From: Dominic Ricottone Date: Fri, 16 Sep 2022 23:03:56 -0500 Subject: [PATCH] Rewrote parts of untar, mktar,, tarcat, etc Added --to-directory and --from-directory options throughout. Added support for a variety of other archive file types. --- archives/archive.bash | 35 ++++++-- archives/completion.bash | 10 +-- archives/mktar | 24 ++++-- archives/mktar-batch | 41 +++++++--- archives/unarchive.bash | 168 +++++++++++++++++++++++++++++++++++++-- archives/untar | 11 ++- 6 files changed, 254 insertions(+), 35 deletions(-) mode change 100644 => 100755 archives/archive.bash diff --git a/archives/archive.bash b/archives/archive.bash old mode 100644 new mode 100755 index e406ca6..61455ed --- a/archives/archive.bash +++ b/archives/archive.bash @@ -16,6 +16,7 @@ archive() { local archive_flags="" local archive_late_flags="" local archive_fn="" + local from_directory="" local positional=() @@ -191,8 +192,13 @@ archive() { error_msg "unsupported archive type '${1}'" ;; + --directory) + from_directory="$2" + shift; shift + ;; + --name) - archive_fn="$2" + archive_fn="$(realpath "$2")" shift; shift ;; @@ -202,7 +208,7 @@ archive() { ;; *) - positional+=("$1") + positional+=("$(realpath "$1")") shift ;; esac @@ -214,12 +220,23 @@ archive() { usage_msg fi - # error if input file does not exist + local targets=() for target in "${positional[@]}"; do + # error if target file does not exist if [[ ! -f "$target" ]] && [[ ! -d "$target" ]]; then error_msg "no such file '${target}'" fi + + # make paths relative to from_directory + if [[ ! -z "$from_directory" ]]; then + targets+="$(realpath --relative-base="$from_directory" "$target")" + else + targets+="$(realpath --relative-base="$(pwd)" "$target")" + fi done + debug_msg "Input files have been resolved to relative paths" + debug_msg "Was:${positional[@]}" + debug_msg "Is now:${targets[@]}" # error if no archive file given if [[ -z "$archive_fn" ]]; then @@ -250,15 +267,19 @@ archive() { if [[ "$verbose" -ne 1 ]]; then verbosity=/dev/null elif [[ "$encrypt" -eq 1 ]]; then - debug_msg "$archive_cmd $archive_flags $archive_late_flags ${positional[@]} 2>$verbosity | $encrypt_cmd $encrypt_flags $archive_fn $passphrase_flags 2>$verbosity" + debug_msg "cd ${from_directory:-.} && $archive_cmd $archive_flags $archive_late_flags ${targets[@]} 2>$verbosity | $encrypt_cmd $encrypt_flags $archive_fn $passphrase_flags 2>$verbosity" else - debug_msg "$archive_cmd $archive_flags $archive_fn $archive_late_flags ${positional[@]} 2>$verbosity" + debug_msg "cd ${from_directory:-.} && $archive_cmd $archive_flags $archive_fn $archive_late_flags ${targets[@]} 2>$verbosity" fi # archive routine + if [[ ! -z "$from_directory" ]] && ! cd "$from_directory" >/dev/null 2> >(/usr/bin/sed -e "s/.*cd/cd/" >$verbosity); then + error_msg "could not access directory '${from_directory}'" + echo "ping" + fi if [[ "$encrypt" -eq 1 ]]; then ( \ - $archive_cmd $archive_flags $archive_late_flags "${positional[@]}" 2>$verbosity \ + $archive_cmd $archive_flags $archive_late_flags "${targets[@]}" 2>$verbosity \ || error_msg "could not archive '${archive_fn}'" \ ) \ | ( \ @@ -266,7 +287,7 @@ archive() { || error_msg "could not encrypt '${archive_fn}'" \ ) else - $archive_cmd $archive_flags "$archive_fn" $archive_late_flags "${positional[@]}" 2>$verbosity \ + $archive_cmd $archive_flags "$archive_fn" $archive_late_flags "${targets[@]}" 2>$verbosity \ || error_msg "could not archive '${archive_fn}'" fi } diff --git a/archives/completion.bash b/archives/completion.bash index 9d36596..20889c7 100644 --- a/archives/completion.bash +++ b/archives/completion.bash @@ -27,9 +27,9 @@ complete -o default -f -X '!*.@(zip|cbr|epub)' rmzip complete -o default -f -X '!*.@(zip|cbr|epub)' zipls # rmtar, tarcat, tarls, untar -# complete with filenames like '*.@(tar|tar.@(gz|xz|zst|bz2)|tar.@(gz|xz|zst|bz2).(gpg|age))' -complete -o default -f -X '!*.@(tar|tar.@(gz|xz|zst|zstd|bz2)|tar.@(gz|xz|zst|bz2).(gpg|age))' rmtar -complete -o default -f -X '!*.@(tar|tar.@(gz|xz|zst|zstd|bz2)|tar.@(gz|xz|zst|bz2).(gpg|age))' tarcat -complete -o default -f -X '!*.@(tar|tar.@(gz|xz|zst|zstd|bz2)|tar.@(gz|xz|zst|bz2).(gpg|age))' tarls -complete -o default -f -X '!*.@(tar|tar.@(gz|xz|zst|zstd|bz2)|tar.@(gz|xz|zst|bz2).(gpg|age))' untar +# complete with filenames like '*.@(zip|cbz|docx|pptx|xlsx|rar|rpa|7z|tar|tar.@(gz|xz|zst|bz2)|tar.@(gz|xz|zst|bz2).@(gpg|age))' +complete -o default -f -X '!*.@(zip|cbz|docx|pptx|xlsx|rar|rpa|7z|tar|tar.@(gz|xz|zst|zstd|bz2)|tar.@(gz|xz|zst|bz2).@(gpg|age))' rmtar +complete -o default -f -X '!*.@(zip|cbz|docx|pptx|xlsx|rar|rpa|7z|tar|tar.@(gz|xz|zst|zstd|bz2)|tar.@(gz|xz|zst|bz2).@(gpg|age))' tarcat +complete -o default -f -X '!*.@(zip|cbz|docx|pptx|xlsx|rar|rpa|7z|tar|tar.@(gz|xz|zst|zstd|bz2)|tar.@(gz|xz|zst|bz2).@(gpg|age))' tarls +complete -o default -f -X '!*.@(zip|cbz|docx|pptx|xlsx|rar|rpa|7z|tar|tar.@(gz|xz|zst|zstd|bz2)|tar.@(gz|xz|zst|bz2).@(gpg|age))' untar diff --git a/archives/mktar b/archives/mktar index 5267bfc..bb7435d 100755 --- a/archives/mktar +++ b/archives/mktar @@ -6,13 +6,14 @@ read -r -d '' help_message <<-EOF Archive utility Usage: mktar -n OUTFILE [OPTIONS] INFILES Options: - --checksum=ALGO create checksum with [none|sha1|sha256] - -h, --help print this message - -n FILE, --name FILE name of archive - -p PASS, --passphrase PASS passphrase for encryption - -q, --quiet suppress error messages and prompts - -v, --verbose show additional messages - -V, --version print version number and exit + --checksum=ALGO create checksum with [none|sha1|sha256] + -d DIR, --from-directory DIR archive from a directory + -h, --help print this message + -n FILE, --name FILE name of archive + -p PASS, --passphrase PASS passphrase for encryption + -q, --quiet suppress error messages and prompts + -v, --verbose show additional messages + -V, --version print version number and exit EOF source /usr/local/lib/mylib.bash @@ -20,6 +21,7 @@ source /usr/local/lib/archive.bash archive_fn="" checksum=-1 +from_directory="" positional=() quiet=0 verbose=0 @@ -49,6 +51,12 @@ while [[ $# -gt 0 ]]; do error_msg "Unknown checksum '${attempted_checksum}'" ;; + -d|--from-directory) + debug_msg "Setting from_directory option to '${2}' (was ${from_directory})" + from_directory="--directory $2" + shift; shift + ;; + -h|--help) help_msg shift @@ -121,7 +129,7 @@ if [[ "$encrypt" -ge 1 && -z "$passphrase" ]]; then passphrase= fi -if ! archive --archive=$archive_action --name "$archive_fn" $passphrase "${positional[@]}"; then +if ! archive --archive=$archive_action --name "$archive_fn" $passphrase $from_directory "${positional[@]}"; then exit 1 fi diff --git a/archives/mktar-batch b/archives/mktar-batch index 88f06aa..c132a91 100755 --- a/archives/mktar-batch +++ b/archives/mktar-batch @@ -4,18 +4,41 @@ name="mktar-batch" version="1.1" read -r -d '' help_message <<-EOF Archive utility for scripting - Usage: mktar-batch [--compress=ALGO] [--encrypt=ALSO --passphrase PASSWD] [--checksum=ALGO] [--name OUTFILE] INFILES + Usage: mktar-batch [--compress=ALGO] [--encrypt=ALGO [--passphrase PASS]] [--checksum=ALGO] [--name OUTFILE] INFILES Options: - --compress=ALGO compress archive with [none|gzip|xz|zstd|bzip2] - --checksum=ALGO create checksum with [none|sha1|sha256] - --encrypt=ALGO encrypt files with [none|gpg|age] - -h, --help print this message - -n FILE, --name FILE name of backup file (Default: archive.tar) - -p PASSWD, --passphrase PASSWD passphase for encryption - -v, --verbose show additional messages - -V, --version print version number and exit + --compress=ALGO compress archive + --checksum=ALGO create checksum + --encrypt=ALGO encrypt archive + -h, --help print this message + -n FILE, --name FILE name of backup file + -p PASS, --passphrase PASS passphase for encryption + -v, --verbose show additional messages + -V, --version print version number and exit + + Compress algorithms and their ALGO codes: + n/a none or no + GZip gzip or gz + xz zx + ZStd zstd or zst + bzip2 bz2 + + Checksum algorithms and their ALGO codes: + n/a none or no + SHA1 sha1 + SHA256 sha256 + + Encrypt algorithms and their ALGO codes: + n/a none or no + GPG gpg + age age EOF +# TODO: add support for compress algorithms: +# WinRAR rar +# Ren'Py rpa +# PKZip zip +# 7-Zip 7z + source /usr/local/lib/mylib.bash source /usr/local/lib/archive.bash diff --git a/archives/unarchive.bash b/archives/unarchive.bash index f03d083..51c9295 100644 --- a/archives/unarchive.bash +++ b/archives/unarchive.bash @@ -2,10 +2,41 @@ # See tarcat for simple usage +is_flagged_for_list() { + #NOTE: Origins of these flags: + # "t" = tar + # "tf" = tar + # "Ot" = tar + # "Otf" = tar + # "l" = unrar, 7z + # "-l" = unzip, unrpa + if contains "$1" "l" "-l" "t" "tf" "Ot" "Otf"; then + return 0 + fi + return 1 +} + +is_flagged_for_stdout() { + #NOTE: Origins of these flags: + # "Ox" = tar + # "Oxf" = tar + # "p" = unrar + # "-p" = unzip + # "x -so" = 7z + if contains "$1" "p" "-p" "Ox" "Oxf"; then + return 0 + fi + return 1 +} + unarchive() { local tar_bin=/usr/bin/tar local gpg_bin=/usr/bin/gpg local age_bin=age + local unzip_bin=/usr/bin/unzip + local unrar_bin=/usr/bin/unrar + local unrpa_bin=unrpa + local _7z_bin=/usr/bin/7z local decrypt="" local decrypt_cmd="" @@ -15,6 +46,7 @@ unarchive() { local unarchive_cmd="" local unarchive_flags="" local unarchive_late_flags="" + local to_directory="" while [[ $# -gt 0 ]]; do case $1 in @@ -184,12 +216,110 @@ unarchive() { shift ;; + --archive=zip) + decrypt=0 + decrypt_cmd="" + decrypt_flags="" + passphrase_flags="" + unarchive_cmd="${unzip_bin}" + if is_flagged_for_list "$unarchive_flags"; then + unarchive_flags="-l" + elif is_flagged_for_stdout "$unarchive_flags"; then + unarchive_flags="-p" + else + unarchive_flags="" + fi + unarchive_late_flags="" + shift + ;; + + --archive=rar) + decrypt=0 + decrypt_cmd="" + decrypt_flags="" + passphrase_flags="" + unarchive_cmd="${unrar_bin}" + if is_flagged_for_list "$unarchive_flags"; then + unarchive_flags="l" + elif is_flagged_for_stdout "$unarchive_flags"; then + unarchive_flags="p" + else + unarchive_flags="x" + fi + unarchive_late_flags="" + shift + ;; + + --archive=rpa) + decrypt=0 + decrypt_cmd="" + decrypt_flags="" + passphrase_flags="" + unarchive_cmd="${unrpa_bin}" + if is_flagged_for_list "$unarchive_flags"; then + unarchive_flags="-l" + elif is_flagged_for_stdout "$unarchive_flags"; then + error_msg "unrpa does not support --stdout" + else + unarchive_flags="" + fi + unarchive_late_flags="" + shift + ;; + + --archive=7z) + decrypt=0 + decrypt_cmd="" + decrypt_flags="" + passphrase_flags="" + unarchive_cmd="${_7z_bin}" + if is_flagged_for_list "$unarchive_flags"; then + unarchive_flags="l -bso0 -bsp0" + elif is_flagged_for_stdout "$unarchive_flags"; then + unarchive_flags="x -so -bsp0" + else + unarchive_flags="x -bso0 -bsp0" + fi + unarchive_late_flags="" + shift + ;; + --archive=*) error_msg "unsupported archive type '${1}'" ;; + --directory) + to_directory="$2" + shift; shift + ;; + --list) - unarchive_flags="$(/usr/bin/sed -e 's/x/t/' <<<"$unarchive_flags")" + case "$unarchive_cmd" in + "$unzip_bin") + #NOTE: `-l` is incompatible with all other flags, so overwrite + unarchive_flags="-l" + ;; + + "$unrar_bin") + #NOTE: `l` is incompatible with all other flags, so overwrite + unarchive_flags="l" + ;; + + "$unrpa_bin") + #NOTE: `-l` is incompatible with all other flags, so overwrite + unarchive_flags="-l" + ;; + + "$_7z_bin") + #NOTE: `l` is incompatible with all other flags, so overwrite + unarchive_flags="l -bso0 -bsp0" + ;; + + *) + #NOTE: `t` is incompatible with `x` (which I set by default), so partially overwrite + unarchive_flags="$(/usr/bin/sed -e 's/x/t/' <<<"$unarchive_flags")" + ;; + esac shift ;; @@ -199,12 +329,36 @@ unarchive() { ;; --stdout) - unarchive_flags="O${unarchive_flags}" + case "$unarchive_cmd" in + "$unzip_bin") + #NOTE: `-p` is incompatible with all other flags, so overwrite + unarchive_flags="-p" + ;; + + "$unrar_bin") + #NOTE: `p` is incompatible with all other flags, so overwrite + unarchive_flags="p" + ;; + + "$unrpa_bin") + error_msg "unrpa does not support --stdout" + ;; + + "$_7z_bin") + #NOTE: `x` is incompatible with all other flags, so overwrite and append `-so` + unarchive_flags="x -so -bsp0" + ;; + + *) + #NOTE: while the effect is unusual, `O` is not incompatible with `x`/`t`, so append + unarchive_flags="O${unarchive_flags}" + ;; + esac shift ;; *) - archive_fn="$1" + archive_fn="$(realpath "$1")" shift ;; esac @@ -228,12 +382,16 @@ unarchive() { if [[ "$verbose" -ne 1 ]]; then verbosity=/dev/null elif [[ "$decrypt" -eq 1 ]]; then - debug_msg "$decrypt_cmd $decrypt_flags $archive_fn $passphrase_flags 2>$verbosity | $unarchive_cmd $unarchive_flags $unarchive_late_flags 2>$verbosity" + debug_msg "cd ${to_directory:-.} && $decrypt_cmd $decrypt_flags $archive_fn $passphrase_flags 2>$verbosity | $unarchive_cmd $unarchive_flags $unarchive_late_flags 2>$verbosity" else - debug_msg "$unarchive_cmd $unarchive_flags $archive_fn $unarchive_late_flags 2>$verbosity" + debug_msg "cd ${to_directory:-.} && $unarchive_cmd $unarchive_flags $archive_fn $unarchive_late_flags 2>$verbosity" fi # unarchive routine + if [[ ! -z "$to_directory" ]] && ! cd "$to_directory" >/dev/null 2> >(/usr/bin/sed -e "s/.*cd/cd/" >$verbosity); then + error_msg "could not access directory '${to_directory}'" + echo "ping" + fi if [[ "$decrypt" -eq 1 ]]; then ( \ $decrypt_cmd $decrypt_flags $passphrase_flags "$archive_fn" 2>$verbosity \ diff --git a/archives/untar b/archives/untar index 1f65129..32c6c5d 100755 --- a/archives/untar +++ b/archives/untar @@ -7,6 +7,7 @@ help_message=$(/usr/bin/cat <<-EOF Usage: untar TARGET [..] [OPTIONS] Options: -h, --help print this message and exit + -d DIR, --to-directory DIR unarchive to a directory -p PASS, --passphrase PASS passphrase for decryption -q, --quiet suppress error messages -v, --version print version number and exit @@ -19,10 +20,17 @@ source /usr/local/lib/unarchive.bash quiet=0 passphrase="" positional=() +to_directory="" verbose=0 while [[ $# -gt 0 ]]; do case $1 in + -d|--to-directory) + debug_msg "Setting to_directory option to '${2}' (was ${to_directory})" + to_directory="--directory $2" + shift; shift + ;; + -h|--help) help_msg shift @@ -35,6 +43,7 @@ while [[ $# -gt 0 ]]; do ;; -p|--passphrase) + debug_msg "Setting passphrase option to '${2}' (was ${passphrase})" passphrase="--passphrase $2" shift; shift ;; @@ -60,7 +69,7 @@ done code=0 for archive_fn in "${positional[@]}"; do archive_action="$(archive_extension "$archive_fn" | tee >(>&2 /usr/bin/head -n -1) | /usr/bin/tail -n 1)" - if ! unarchive --archive=$archive_action "$archive_fn" $passphrase; then + if ! unarchive --archive=$archive_action "$archive_fn" $passphrase $to_directory; then code=1 fi done -- 2.45.2