diff --git a/dotstow.sh b/dotstow.sh new file mode 100755 index 0000000..c619f95 --- /dev/null +++ b/dotstow.sh @@ -0,0 +1,153 @@ +#!/bin/bash + +stow() { + local stowrepo=$1 + local stowtarget=$2 + + [ "$VERBOSE" -gt 1 ] && echo "Entering $stowrepo..." + + local files=() + while read line; do + if [ "$line" != "." -a "$line" != ".." ]; then + files+=("$line") + fi + done < <(ls -a $stowrepo) + + [ "$VERBOSE" -gt 1 ] && echo "Filelist: ${files[@]}" + + for file in "${files[@]}"; do + local file=$(basename "$file") + local ctarget="$stowtarget/$file" + local csource="$stowrepo/$file" + if [ -e "$ctarget" ]; then + if [ -L "$ctarget" -a "$(readlink -f "$ctarget")" == "$(realpath "$csource")" ]; then # Link already exists. Skip + [ $VERBOSE -gt 0 ] && echo "Skipping $ctarget..." + elif [ -d "$ctarget" -a -d "$csource" ]; then # Dictionary exists. Enter + stow "$csource" "$ctarget" + else # No can do. + echo "Aborting. $ctarget exists, but is not owned by dotstow"; exit 1 + fi + else + if [ "$SIMULATE" -gt 0 ] || ln -s "$(realpath "$csource")" "$ctarget"; then + [ $VERBOSE -gt 0 ] && echo "Linking $ctarget to $csource" + else + echo "Could not link $ctarget to $csource"; exit 1 + fi + fi + done + + [ "$VERBOSE" -gt 1 ] && echo "Leaving $stowrepo..." +} + +destow() { + local stowrepo=$1 + local stowtarget=$2 + + [ "$VERBOSE" -gt 1 ] && echo "Entering $stowrepo..." + + local files=() + while read line; do + if [ "$line" != "." -a "$line" != ".." ]; then + files+=("$line") + fi + done < <(ls -a $stowrepo) + + [ "$VERBOSE" -gt 1 ] && echo "Filelist: ${files[@]}" + + for file in "${files[@]}"; do + local file=$(basename "$file") + local ctarget="$stowtarget/$file" + local csource="$stowrepo/$file" + + if [ ! -e "$ctarget" ]; then # Target doesn't exists. skip + [ $VERBOSE -gt 0 ] && echo "$ctarget does not exist. Skipping..." + elif [ ! -L "$ctarget" ]; then + if [ -d "$ctarget" -a -d "$csource" ]; then + destow "$csource" "$ctarget" + else + echo "$ctarget exists, but is not owned by dotstow (Not a link)"; exit 1 + fi + elif [ "$(readlink -f "$ctarget")" != "$(realpath "$csource")" ]; then + [ "$VERBOSE" -gt 0 ] && echo "$(readlink -f "$ctarget")" != "$(realpath "$csource")" + echo "$ctarget exists, but is not owned by dotstow (Wrong link)"; exit 1 + else + if [ "$SIMULATE" -gt 0 ] || rm -f "$ctarget"; then + [ $VERBOSE -gt 0 ] && echo "Unlinking $ctarget" + else + echo "Could not unlink $ctarget"; exit 1 + fi + fi + done + + [ "$VERBOSE" -gt 1 ] && echo "Leaving $stowrepo..." +} + +restow() { + destow "$1" "$2" + stow "$1" "$2" +} + +TARGET="$HOME" +VERBOSE=0 +REPO="$(dirname $0)" +COMMAND=stow +HELP=0 +SIMULATE=0 + +while getopts DSRvhnt: opt; do + case $opt in + D) + COMMAND=destow + ;; + S) + COMMAND=stow + ;; + R) + COMMAND=restow + ;; + t) + TARGET="$OPTARG" + ;; + v) + let VERBOSE++ + ;; + h) + HELP=1 + ;; + n) + SIMULATE=1 + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + :) + echo "Option -$OPTARG required an argument." >&2 + ;; + esac +done +shift $[ $OPTIND-1 ] + +TARGET=${TARGET/%\//} # Remove trailing slashes + +if [ "$HELP" -eq 1 -o "${#@}" -eq 0 ]; then + echo -e "Usage: ./dotstow.sh [-DSRvhn] [-t target] stows..." + echo -e "\t-D: Destow" + echo -e "\t-S: Stow" + echo -e "\t-R: Restow" + echo -e "\t-t: target folder (default ~)" + echo -e "\t-v: verbose" + echo -e "\t-h: show this help" + echo -e "\t-n: simulated/dry-run" + echo -e "\tstows: stow folders relative to dotstow.sh" +fi + +for stow in $@; do + if [ ! -d "$REPO/$stow" ]; then + echo "Stow $stow not found." >&2 + exit 2 + fi + $COMMAND "$REPO/$stow" "$TARGET" +done + +[ "$SIMULATE" -gt 0 ] && echo "Warning: Simulated. No actual changes to the filesystem have been done."