summaryrefslogtreecommitdiff
path: root/backomp
blob: 750576be951d6b53234402e2a93a832be77a539f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!/usr/bin/env bash

set -e

! read -rd '' FILTER_AWK <<'EOF'
function flr(n, s) { return int(n / s) * s }

BEGIN {
	split("h d w m", buckets)
	for (i in buckets) {
		if (match(retain, "([*0-9]+)" buckets[i], md) && md[1])
			ret[buckets[i]] = md[1]

		if (index(replace, buckets[i]))
			repl[buckets[i]] = 1
	}
}

{
	t = mktime(gensub(/(.*\/|^)(.+)-(..)-(..)-(..)(..)(..)/,
		"\\2 \\3 \\4 \\5 \\6 \\7", 1), 1)

	bt["h"] = flr(t, 60*60)
	bt["d"] = flr(t, 24*60*60)
	bt["m"] = mktime(strftime("%Y %m", t, 1) " 01 00 00 00", 1)
	bt["w"] = bt["m"] + flr(t - bt["m"], 7*24*60*60)

	for (i = 1; i <= length(buckets); ++i) {
		b = buckets[i]

		if (!ret[b] || (b in repl && (b, bt[b]) in bkeep))
			continue

		if ((b, bt[b]) in bkeep || ret[b] == "*" || bc[b]++ < ret[b])
			bkeep[b, bt[b]] = $1
	}
}

END {
	for (i in bkeep)
		++keep[bkeep[i]]

	asorti(keep)
	for (i in keep) print keep[i]
}
EOF

run() {
	echo "* ${@}"
	"${@}"
}

backup() {
	dest_dir="${1}"
	src_path="${2}"
	retain="${3:-"*h*d*w*m"}"
	replace="${4:-"h"}"

	[[ "$((dcount++))" -ne 0 ]] && echo
	echo "[${dcount}] [${retain}] ${src_path} => ${dest_dir}"
	mapfile -t snaps < <(shopt -s nullglob;
		printf '%s\n' "${dest_dir}/"* | sort -r | head -c -1)

	dest_path="${dest_dir}/$(date -u '+%Y-%m-%d-%H%M%S')"
	run rsync -ac --mkpath ${snaps[0]:+"--link-dest=${snaps[0]}"} \
		"${src_path}" "${dest_path}"
	snaps=("${dest_path}" "${snaps[@]}")

	mapfile -t keep < <(
		awk -v "retain=${retain}" -v "replace=${replace}" \
			"${FILTER_AWK}" < <(printf '%s\n' "${snaps[@]}"))
	if [[ -z "${keep}" ]]; then
		echo "WARNING: dirs to keep shouldn't be empty; skipping" >&2
		return
	fi

	for ((i = ${#snaps[@]} - 1, ki = 0; i >= 0; --i)); do
		if [[ "${snaps[i]}" == "${keep[ki]}" ]]; then
			((++ki))
		else
			run rm -r "${snaps[i]}"
		fi
	done

	printf 'kept %s\n' "${keep[@]}"
}

declare -A opts
while IFS='=' read -r f1 f2; do
	[[ -z "${f1}" ]] && continue

	if [[ "${f1}" =~ ^\[(.*)]$ ]]; then
		base_path="${BASH_REMATCH[1]}"
		opts=()
	elif [[ "${f1}" == '$'* ]]; then
		opts["${f1:1}"]="${f2}"
	else
		backup "${base_path}/${f1}" "${f2}" \
			"${opts['retain']}" "${opts['replace']}"
	fi
done < "${1}"