2
0
mirror of https://github.com/koreader/koreader synced 2024-10-31 21:20:20 +00:00
koreader/tools/mk7z.sh
Benoit Pierre a4400b3ccb tools/mk7z: support debian unstable / ubuntu 22.04
The version of `7z` provided by `p7zip-full` is now the same as `7zip`:

- symlinks are dereferenced by default (no support for `-l`)
- `7z -ba h` appends a trailing `/` to directories
  (not present in the output of `7z -slt l`)
2024-05-12 09:47:44 +02:00

94 lines
2.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -eo pipefail
# set -x
[[ $# -ge 3 ]]
archive="$(realpath "$1")"
epoch="$2"
shift 2
options=()
for a in "$@"; do
shift
case "${a}" in
--) break ;;
-*) options+=("${a}") ;;
*) break ;;
esac
done
[[ $# -gt 0 ]]
patterns=("$@")
# We need to use the full path to the executable to avoid
# a weird issue when using the p7zip project pre-built
# binary (`Can't load './7z.dll' (7z.so)...`).
sevenzip="$(which 7z)"
# echo "archive : ${archive}"
# echo "epoch : ${epoch}"
# echo "options : ${options[@]}"
# echo "patterns: ${patterns[@]}"
tmpdir="$(mktemp -d -t tmp7z.XXXXXXXXXX)"
trap 'rm -rf "${tmpdir}"' EXIT
manifest="${tmpdir}/manifest"
# Detect if that version of 7z deferences symlinks by default.
sevenzip_manifest_cmd=("${sevenzip}" -ba h)
ln -s /dev/null "${tmpdir}/symlink"
checksum="$("${sevenzip_manifest_cmd[@]}" "${tmpdir}/symlink" | awk '{ print $1 }')"
if [[ "${checksum}" != '00000000' ]]; then
sevenzip_manifest_cmd+=(-l)
fi
rm -f "${tmpdir}/symlink"
# Note: remove trailing `/` appended to directories in some 7z versions.
"${sevenzip_manifest_cmd[@]}" "${patterns[@]}" |
awk '{ if ($3!="") print $3, $2, $1; else { gsub("/$", "", $1); print $1 } }' |
sort >"${manifest}"
# cat "${manifest}" | less
if [[ -r "${archive}" ]]; then
if diff --brief --label 'in archive' \
<(
"${sevenzip}" -slt l "${archive}" |
awk '
/^([^=]+) = / { entry[$1] = $3; }
/^CRC =/ { if ($3!="") print entry["Path"], entry["Size"], $3; else print entry["Path"] }
' | sort
) --label 'to add' "${manifest}"; then
exit
fi
# There's no 7z option to overwrite the archive
# if it already exists (instead of updating it)…
rm -f "${archive}"
fi
# Extract list of paths from manifest.
rev <"${manifest}" | cut -f3- -d' ' | rev >"${tmpdir}/paths"
# Quick sanity check: no path outside the current directory.
if grep '^(/|\.\./)' <"${tmpdir}/paths"; then
echo "^ some paths are outside the current directory!"
exit 1
fi
# Make a copy of everything so we can later
# patch timestamp to ensure reproducibility.
mkdir "${tmpdir}/contents"
# We want to copy "empty" (with ignored files) directories.
tar --dereference --no-recursion --create \
--verbatim-files-from --files-from="${tmpdir}/paths" |
tar --extract --directory="${tmpdir}/contents"
cd "${tmpdir}/contents"
# Fix timestamps.
find . -depth -print0 | xargs -0 touch --date="${epoch}"
# And create the final archive.
"${sevenzip}" -mqs "${options[@]}" a "${archive}" .
# vim: sw=4