#!/bin/bash
name="mktar"
version="1.3"
read -r -d '' help_message <<-EOF
Archive utility
Usage: mktar [OPTIONS] INFILES
Options:
-c, --compress compress archive with gzip
--compress=ALGO compress archive with [none|gzip|xz|zstd|bzip2]
-C, --checksum create checksum with SHA1
--checksum=ALGO create checksum with [none|sha1|sha256]
-e, --encrypt encrypt files with GPG
--encrypt=ALGO encrypt files with [none|gpg|age]
-h, --help print this message
-n FILE, --name FILE name of backup file (Default: archive.tar)
-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
positional=()
quiet=0
verbose=0
compress=-1
encrypt=-1
checksum=-1
archive_fn=""
while [[ $# -gt 0 ]]; do
case $1 in
--compress=gz|--compress=gzip|--compress=1)
debug_msg "Setting compress option to 1 (=gzip) (was ${compress})"
compress=1
shift
;;
--compress=xz|--compress=2)
debug_msg "Setting compress option to 2 (=xz) (was ${compress})"
compress=2
shift
;;
--compress=zst|--compress=zstd|--compress=3)
debug_msg "Setting compress option to 3 (=zstd) (was ${compress})"
compress=3
shift
;;
--compress=bz2|--compress=bzip2|--compress=4)
debug_msg "Setting compress option to 4 (=bzip2) (was ${compress})"
compress=4
shift
;;
--compress=no|--compress=none|--compress=0)
debug_msg "Setting compress option to 0 (=none) (was ${compress})"
compress=0
shift
;;
--compress=*)
attempted_compression="$(/usr/bin/printf "%s\n" "$1" | sed -e 's/^.*=//' )"
error_msg "Unknown compression '${attempted_compression}'"
;;
-c|--compress)
debug_msg "Setting compress option to 1 (=gzip) (was ${compress})"
compress=1
shift
;;
--checksum=sha1|--checksum=1)
debug_msg "Setting checksum option to 1 (=sha1) (was ${checksum})"
checksum=1
shift
;;
--checksum=sha256|--checksum=2)
debug_msg "Setting checksum option to 2 (=sha256) (was ${checksum})"
checksum=2
shift
;;
--checksum=no|--checksum=none|--checksum=0)
debug_msg "Setting checksum option to 0 (=none) (was ${checksum})"
checksum=0
shift
;;
--checksum=*)
attempted_checksum="$(/usr/bin/printf "%s\n" "$1" | sed -e 's/^.*=//' )"
error_msg "Unknown checksum '${attempted_checksum}'"
;;
-C|--checksum)
debug_msg "Setting checksum option to 1 (=SHA1) (was ${checksum})"
checksum=1
shift
;;
--encrypt=gpg|--encrypt=1)
debug_msg "Setting encrypt option to 1 (=GPG) (was ${encrypt})"
encrypt=1
shift
;;
--encrypt=age|--encrypt=2)
debug_msg "Setting encrypt option to 2 (=Age) (was ${encrypt})"
encrypt=2
shift
;;
--encrypt=no|--encrypt=none|--encrypt=0)
debug_msg "Setting encrypt option to 0 (=none) (was ${encrypt})"
encrypt=0
shift
;;
--encrypt=*)
attempted_encryption="$(/usr/bin/printf "%s\n" "$1" | sed -e 's/^.*=//' )"
error_msg "Unknown encryption '${attempted_encryption}'"
;;
-e|--encrypt)
debug_msg "Setting encrypt option to 1 (=GPG) (was ${encrypt})"
encrypt=1
shift
;;
-h|--help)
help_msg
shift
;;
-n|--name)
debug_msg "Setting output filename to ${2} (was ${archive_fn})"
archive_fn="$2"
shift; shift
;;
-q|--quiet)
debug_msg "Setting quiet option to 1 (was ${quiet})"
quiet=1
shift
;;
-v|--verbose)
debug_msg "Setting verbose option to 1 (was ${verbose})"
verbose=1
shift
;;
-V|--version)
version_msg
;;
*)
debug_msg "Argument '${1}' added to positional array"
positional+=("$1")
shift
;;
esac
done
# error if no filenames given
if [[ "${#positional[@]}" -lt 1 ]]; then
debug_msg "No input filenames were given"
usage_msg
fi
# determine compress action
if [[ "$compress" -eq 1 ]]; then
archive_action="tar.gz"
elif [[ "$compress" -eq 2 ]]; then
archive_action="tar.xz"
elif [[ "$compress" -eq 3 ]]; then
archive_action="tar.zst"
elif [[ "$compress" -eq 4 ]]; then
archive_action="tar.bz2"
elif [[ "$compress" -eq -1 ]]; then
debug_msg "No explicit compress option; parsing archive name..."
case "$archive_fn" in
*.tar)
debug_msg "Detected tarball (no compress)"
archive_action="tar"
;;
*.tar.gz)
debug_msg "Detected gzip compress"
archive_action="tar.gz"
;;
*.tar.xz)
debug_msg "Detected xz compress"
archive_action="tar.xz"
;;
*.tar.zst)
debug_msg "Detected zstandard compress"
archive_action="tar.zst"
;;
*.tar.bz2)
debug_msg "Detected bz2 compress"
archive_action="tar.bz2"
;;
*)
debug_msg "Could not parse archive name; defaulting to no compress"
archive_action="tar"
;;
esac
else
archive_action="tar"
fi
# determine encrypt action
if [[ "$encrypt" -eq 1 ]]; then
archive_action="${archive_action}.gpg"
elif [[ "$encrypt" -eq 2 ]]; then
archive_action="${archive_action}.age"
elif [[ "$encrypt" -eq -1 ]]; then
debug_msg "No explicit encrypt option; parsing archive name..."
case "$archive_fn" in
*.gpg)
debug_msg "Detected gpg encrypt"
archive_action="${archive_action}.gpg"
;;
*.age)
debug_msg "Detected age encrypt"
archive_action="${archive_action}.age"
;;
*)
debug_msg "No explicit encrypt option and could not parse archive name; defaulting to no encrypt"
archive_action="${archive_action}"
;;
esac
else
archive_action="${archive_action}"
fi
# determine checksum action
if [[ "$checksum" -eq 1 ]]; then
checksum_action="sha1"
elif [[ "$checksum" -eq 2 ]]; then
checksum_action="sha256"
fi
# output filename
if [[ -z "$archive_fn" ]]; then
archive_fn="archive.${archive_action}"
checksum_fn="archive.${checksum_action}"
debug_msg "No output filename was given, defaulting to '${archive_fn}'"
else
checksum_fn="$(fn_basename "$archive_fn").${checksum_action}"
fi
# check files
if contains "$archive_fn" "${positional[@]}"; then
error_msg "Output file cannot also be input file ('${archive_fn}')"
elif [[ "$checksum" -ge 1 ]] && contains "$checksum_fn" "${positional[@]}"; then
error_msg "Output file cannot also be input file ('${checksum_fn}')"
fi
if ! prompt_overwrite "$archive_fn"; then
exit 1
elif [[ "$checksum" -ge 1 ]] && ! prompt_overwrite "$checksum_fn"; then
exit 1
fi
for target in "${positional[@]}"; do
if [[ ! -f "$target" ]] && [[ ! -d "$target" ]]; then
error_msg "No such file '${target}'"
fi
done
# main routine
code=0
case "$archive_action" in
tar)
if ! /usr/bin/tar -cf "$archive_fn" "${positional[@]}"; then
code=1
fi
;;
tar.gpg)
if ! /usr/bin/tar -c "${positional[@]}" | /usr/bin/gpg --symmetric --output "$archive_fn"; then
code=1
fi
;;
tar.age)
if ! /usr/bin/tar -c "${positional[@]}" | /usr/bin/age --encrypt --passphrase --output "$archive_fn"; then
code=1
fi
;;
tar.gz)
if ! /usr/bin/tar -czf "$archive_fn" "${positional[@]}"; then
code=1
fi
;;
tar.gz.gpg)
if ! /usr/bin/tar -cz "${positional[@]}" | /usr/bin/gpg --symmetric --output "$archive_fn"; then
code=1
fi
;;
tar.gz.age)
if ! /usr/bin/tar -cz "${positional[@]}" | /usr/bin/age --encrypt --passphrase --output "$archive_fn"; then
code=1
fi
;;
tar.xz)
if ! /usr/bin/tar -cJf "$archive_fn" "${positional[@]}"; then
code=1
fi
;;
tar.xz.gpg)
if ! /usr/bin/tar -cJ "${positional[@]}" | /usr/bin/gpg --symmetric --output "$archive_fn"; then
code=1
fi
;;
tar.xz.age)
if ! /usr/bin/tar -cJ "${positional[@]}" | /usr/bin/age --encrypt --passphrase --output "$archive_fn"; then
code=1
fi
;;
tar.zst)
if ! /usr/bin/tar --zstd -cf "$archive_fn" "${positional[@]}"; then
code=1
fi
;;
tar.zst.gpg)
if ! /usr/bin/tar --zstd -c "${positional[@]}" | /usr/bin/gpg --symmetric --output "$archive_fn"; then
code=1
fi
;;
tar.zst.age)
if ! /usr/bin/tar --zstd -c "${positional[@]}" | /usr/bin/age --encrypt --passphrase --output "$archive_fn"; then
code=1
fi
;;
tar.bz2)
if ! /usr/bin/tar -cjf "$archive_fn" "${positional[@]}"; then
code=1
fi
;;
tar.bz2.gpg)
if ! /usr/bin/tar -cj "${positional[@]}" | /usr/bin/gpg --symmetric --output "$archive_fn"; then
code=1
fi
;;
tar.bz2.age)
if ! /usr/bin/tar -cj "${positional[@]}" | /usr/bin/age --encrypt --passphrase --output "$archive_fn"; then
code=1
fi
;;
esac
# checksum routine
if [[ "$checksum" -ge 1 ]]; then
if [[ -f "$archive_fn" ]]; then
case "$checksum_action" in
sha1)
if ! /usr/bin/sha1sum "$archive_fn" | /usr/bin/awk '{print $1}' > "$checksum_fn"; then
code=1
fi
;;
sha256)
if ! /usr/bin/sha256sum "$archive_fn" | /usr/bin/awk '{print $1}' > "$checksum_fn"; then
code=1
fi
;;
esac
else
error_msg "No such file '${archive_fn}'"
fi
fi
# return stored code
exit "$code"