M archives/archive.bash => archives/archive.bash +28 -7
@@ 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
}
M archives/completion.bash => archives/completion.bash +5 -5
@@ 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
M archives/mktar => archives/mktar +16 -8
@@ 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
M archives/mktar-batch => archives/mktar-batch +32 -9
@@ 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
M archives/unarchive.bash => archives/unarchive.bash +163 -5
@@ 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 \
M archives/untar => archives/untar +10 -1
@@ 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