dotfiles/packages/plow/plow.bash
2025-03-25 18:48:52 +01:00

121 lines
2.2 KiB
Bash
Executable file

#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
progname="$0"
error() {
local line
for line in "$@"; do
echo "$progname: $line" 1>&2
done
exit 1
}
shopt -s nullglob globstar
if [[ ! -v PLOW_CACHE || -z $PLOW_CACHE ]]; then
PLOW_CACHE=.plowcache
fi
args=$(
getopt \
--options f:t:Fivm: \
--longoptions from:,to:,force,interactive,verbose,directory-mode: \
--name "$0" \
-- "$@"
)
eval set -- "$args"
from=${PLOW_FROM:-$PWD}
to=${PLOW_TO:-$HOME}
lnflags=()
mkdirflags=()
rmflags=()
while true; do
case "$1" in
-f | --from)
from=$2
shift 2
;;
-t | --to)
to=$2
shift 2
;;
-F | --force)
lnflags+=(--force)
rmflags+=(--force)
shift
;;
-i | --interactive)
lnflags+=(--interactive)
rmflags+=(--interactive)
shift
;;
-v | --verbose)
lnflags+=(--verbose)
mkdirflags+=(--verbose)
rmflags+=(--verbose)
shift
;;
-m | --directory-mode)
mkdirflags+=(--mode "$2")
shift 2
;;
--)
shift
break
;;
esac
done
from=$(realpath --strip -- "$from")
to=$(realpath --strip -- "$to")
if (($# > 0)); then
choices=("$@")
else
choices=()
for dir in "$from"/*/; do
choices+=("$(basename -- "$dir")")
done
fi
shopt -s dotglob
cache=()
if [[ -n $PLOW_CACHE ]]; then
if [[ -r $PLOW_CACHE ]]; then
while IFS= read -r link; do
cache+=("$link")
done <"$PLOW_CACHE"
fi
: >"$PLOW_CACHE"
fi
for choice in "${choices[@]}"; do
prefix=$from/$choice
for target in "$prefix"/**/*; do
if [[ -f $target ]]; then
link=$to${target#"$prefix"}
parent=$(dirname -- "$link")
mkdir --parents "${mkdirflags[@]}" -- "$parent"
ln --symbolic "${lnflags[@]}" -- "$target" "$link"
if [[ -n "$PLOW_CACHE" ]]; then
echo "$link" >>"$PLOW_CACHE"
fi
fi
done
done
for link in "${cache[@]}"; do
if [[ -L "$link" && ! -f "$link" ]]; then
rm "${rmflags[@]}" -- "$link"
fi
done