dotfiles/stow.sh

227 lines
5.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# From: github.com/mikepqr/stowsh
# Author: Mike Lee Williams
# License: GPL3
_runcommands() {
if [[ $DRYRUN == 1 ]] || [[ $VERBOSE -gt 1 ]]; then
echo "$@"
fi
if [[ $DRYRUN != 1 ]]; then
eval "$@"
fi
}
echoerr() {
printf "%s\n" "$*" >&2
}
deperr() {
echoerr "stowsh requires $1"
}
isgnu() {
if $1 --version >/dev/null 2>&1; then
return 1
else
return 0
fi
}
stowsh_setpaths() {
if command -v grealpath >/dev/null 2>&1; then
rpcmd="grealpath"
elif command -v realpath >/dev/null 2>&1; then
rpcmd="realpath"
else
deperr "GNU coreutils"
return 1
fi
if isgnu $rpcmd; then
deperr "GNU coreutils"
return 1
fi
if command -v gfind >/dev/null 2>&1; then
findcmd="gfind"
elif command -v find >/dev/null 2>&1; then
findcmd="find"
else
deperr "GNU findutils"
return 1
fi
if isgnu $findcmd; then
deperr "GNU findutils"
return 1
fi
if [[ "${USEGIT}" -eq 1 ]]; then
flist="list_git_files"
else
flist="list_files"
fi
}
list_files() {
if [[ "$1" == "-d" ]]; then
$findcmd . -mindepth 1 -type d -printf "%P\0"
else
$findcmd . -type f -printf "%P\0" -or -type l -printf "%P\0"
fi
}
list_git_files() {
gitfiles=($(git ls-files))
if [[ "${#gitfiles[@]}" == 0 ]]; then return; fi
if [[ "$1" == "-d" ]]; then
printf "%s\n" "${gitfiles[@]}" |
xargs -n1 dirname |
sort | uniq |
sed '/^\.$/d' |
tr '\n' '\0'
else
printf "%s\0" "${gitfiles[@]}"
fi
}
stowsh_install() {
stowsh_setpaths || return 1
local pkg=$1
local target
target=$("$rpcmd" "${2-$PWD}")
local commands=()
cd "$pkg" || return 1
mkdir -p "$target"
while IFS= read -r -d '' d; do
commands+=("mkdir -p '$target/$d'")
done < <($flist -d)
while IFS= read -r -d '' f; do
local targetf="$target/$f"
local thisdir
thisdir=$(dirname "$targetf")
local relative
relative=$($rpcmd "$f" --relative-to="$thisdir" --canonicalize-missing)
if [[ ! -f "$targetf" ]]; then
commands+=("ln -s '$relative' '$targetf'")
else
echoerr "$targetf already exists."
if [[ ! $SKIP -eq 1 ]]; then
echoerr "Aborting. Rerun with the -s flag to skip errors."
return 1
fi
fi
done < <($flist)
for cmd in "${commands[@]}"; do
_runcommands "$cmd"
done
}
stowsh_uninstall() {
stowsh_setpaths || return 1
local pkg=$1
local target
target=$("$rpcmd" "${2-$PWD}")
local commands=()
cd "$pkg" || return 1
while IFS= read -r -d '' f; do
local targetf="$target/$f"
if [[ $($rpcmd "$targetf") == $($rpcmd "$f") ]]; then
commands+=("rm '$targetf'")
elif [[ -f "$targetf" ]]; then
echoerr "$targetf does not point to to $($rpcmd "$f")."
if [[ ! $SKIP -eq 1 ]]; then
echoerr "Aborting. Rerun with the -s flag to skip errors."
return 1
fi
elif [[ ! -f "$targetf" ]]; then
echoerr "$targetf does not exist. Nothing to do."
if [[ ! $SKIP -eq 1 ]]; then
echoerr "Aborting. Rerun with the -s flag to skip errors."
return 1
fi
fi
done < <($flist)
while IFS= read -r -d '' d; do
commands+=("[[ -d '$target/$d' ]] && $findcmd '$target/$d' -type d -empty -delete")
done < <($flist -d)
for cmd in "${commands[@]}"; do
_runcommands "$cmd"
done
}
stowsh_help() {
echo "Usage: $0 [-D] [-n] [-s] [-g] [-v[v]] [-t TARGET] PACKAGES..."
}
if [ "$0" = "$BASH_SOURCE" ]; then
UNINSTALL=0
DRYRUN=0
SKIP=0
USEGIT=0
TARGET="$PWD"
PACKAGES=()
while [[ "$@" ]]; do
if [[ $1 =~ ^- ]]; then
OPTIND=1
while getopts ":vhDsngt:" opt; do
case $opt in
h)
stowsh_help
exit 0
;;
D)
UNINSTALL=1
;;
n)
DRYRUN=1
;;
s)
SKIP=1
;;
v)
VERBOSE=$((VERBOSE + 1))
;;
g)
USEGIT=1
;;
t)
TARGET="$OPTARG"
;;
*)
echo "'$OPTARG' is an invalid option/flag"
exit 1
;;
esac
done
shift $((OPTIND - 1))
else
PACKAGES+=("$1")
shift
fi
done
if [[ ${#PACKAGES[@]} -eq 0 ]]; then
stowsh_help
exit 1
fi
for i in ${!PACKAGES[*]}; do
pkg=${PACKAGES[$i]}
if [[ $UNINSTALL -eq 1 ]]; then
if [[ $VERBOSE -gt 0 ]]; then echoerr "Uninstalling $pkg from $TARGET"; fi
stowsh_uninstall "$pkg" "$TARGET"
else
if [[ $VERBOSE -gt 0 ]]; then echoerr "Installing $pkg to $TARGET"; fi
stowsh_install "$pkg" "$TARGET"
fi
done
fi