diff options
| Commit | 375d11b33142e971a82f28ddb4cc7d386e3551cf (patch) | |
|---|---|---|
| Author | 2026-11-01 17:50:07 +0200 | |
| Committer | 2026-11-01 17:50:07 +0200 | |
| Files | 85373ae415fcff7142ee1194594e8bb1e381176b /make | |
| .files-375d11b33142e971a82f28ddb4cc7d386e3551cf.tar .files-375d11b33142e971a82f28ddb4cc7d386e3551cf.tar.gz .files-375d11b33142e971a82f28ddb4cc7d386e3551cf.zip | ||
| Parent | e41b78ee956a6adcfc07b3c453f70198214ca599 (diff) | |
feat!: Rework how .files are managed
1 files changed, 183 insertions, 0 deletions
| -rwxr-xr-x | make | 183 |
@@ -0,0 +1,183 @@ +#!/bin/sh +# IMPORTANT NOTE: The "local" keyword is not defined in POSIX, so I've decided to work without it. +# For this reason, I do "$X/$f" instead of assigning local variables. +# Another thing to consider is that in recursive functions, backtracking logic +# CANNOT depend on variables, which are changed somewhere in the body before +# the recursive step. + +cd "${0%/*}" + +[ -z "$HOSTNAME" ] && HOSTNAME="$(hostname)" +if [ -z "$HOSTNAME" ] +then + echo "Set HOSTNAME variable to the system's name!" >/dev/stderr + exit 1 +fi +if [ ! -f "./SYSTEM/$HOSTNAME" ] +then + echo "No configuration in ./SYSTEM for '$HOSTNAME'!" >/dev/stderr + exit 1 +fi + +# __merge_link <.FILES_DIR> <HOME_DIR> +__merge_link() { + while read -r f + do + # Output file doesn't exist -> Create link + # This is also true on broken symlinks + if [ ! -e "$2/$f" ] + then + if [ -h "$2/$f" ] + then + echo "Warn: '$2/$f' is a broken link to '$(readlink "$2/$f")', updating!" + rm "$2/$f" + fi + ln -s "$1/$f" "$2/$f" + # Output file is already linked to current -> Do nothing + elif [ -h "$2/$f" -a "$(readlink $2/$f)" = "$1/$f" ] + then + : + # Current file is a file (and output exists) -> Create link but also update current + elif [ -f "$1/$f" ] + then + if [ -f "$2/$f" ] + then + # When output is NOT a link inside .files, + # update current file with the contents of the output + # Otherwise, + # replace output file with (link to) current + if ! cmp -s "$1/$f" "$2/$f" + then + if [ -h "$2/$f" ] + then + real="$(readlink "$2/$f")" + if [ "${real#$PWD}" = "$real" ] + then + cp "$real" "$1/$f" 2>/dev/null + echo "Warn: '$2/$f' already existed, '$1/$f' updated with its contents!" >/dev/stderr + fi + else + cp "$2/$f" "$1/$f" + echo "Warn: '$2/$f' already existed, '$1/$f' updated with its contents!" >/dev/stderr + fi + fi + rm "$2/$f" + + ln -s "$1/$f" "$2/$f" + else + echo "Error: '$2/$f' already exists and is not a regular file!" + fi + # Current file is a directory (and output exists) -> Link current's files & dirs in output directory + elif [ -d "$1/$f" ] + then + if [ -d "$2/$f" ] + then + # If output is a link to directory in .files, continuing with merge + # will place links inside a directory in .files, which is bad. + # Instead, we'll make the link to directory become a directory with + # links to all inner files. + if [ -h "$2/$f" ] + then + real="$(realpath "$2/$f")" + if [ "${real#$PWD}" != "$real" ] + then + rm "$2/$f" + mkdir "$2/$f" + while read -r ff + do + ln -s "$real/$ff" "$2/$f/$ff" + done <<FILES +$(ls -A1 "$real") +FILES + fi + fi + __merge_link "$1/$f" "$2/$f" + else + echo "Error: '$2/$f' already exists and is not a directory!" + fi + else + echo "Error: '$1/$f' is neither a file or directory!" + fi + done <<FILES +$(ls -A1 "$1") +FILES +} + +link() { + while read -r dir + do + dir="${dir%%*/}" + if [ ! -d "./$dir" ] + then + echo "Unknown directory '$dir'!" >/dev/stderr + continue + fi + + __merge_link "$PWD/$dir" "$HOME" + echo "Linked $dir" + done <"./SYSTEM/$HOSTNAME" +} + +# __remove_link <.FILES_DIR> <HOME_DIR> +__remove_link() { + while read -r f + do + if [ -h "$2/$f" ] + then + real="$(realpath "$2/$f")" + # Output file exists and is a link to current -> Remove output + if [ "$real" = "$1/$f" ] + then + rm "$2/$f" + continue + # Output file exists and is a link in .files -> Do nothing (do not remove from .files) + # This is possible in the case when we have .files/X/A and .files/Y/A, and we linked + # .files/X but are now unlinking .files/Y + elif [ "${real#$PWD}" != "$real" ] + then + continue + fi + fi + if [ -d "$1/$f" -a -d "$2/$f" ] + then + __remove_link "$1/$f" "$2/$f" + fi + done <<FILES +$(ls -A1 "$1") +FILES + # See IMPORTANT NOTE, since we're aiming for full POSIX compatibility, + # the variable f will be overwritten by recursive calls, so this loop + # cannot be merged with the loop above (remove dirs on backtracking) + while read -r f + do + # Output file exists and is an empty directory -> Remove it + # We're assuming this directory was created by the script and/or the user + # will not care for empty dirs + if [ -d "$2/$f" -a -z "$(ls -A "$2/$f" 2>/dev/null)" ] + then + rm -r "$2/$f" + fi + done <<FILES +$(ls -A1 "$1") +FILES +} + +unlink() { + while read -r dir + do + dir="${dir%%*/}" + if [ ! -d "./$dir" ] + then + echo "Unknown directory '$dir'!" >/dev/stderr + continue + fi + + __remove_link "$PWD/$dir" "$HOME" + echo "Unlinked $dir" + done <"./SYSTEM/$HOSTNAME" +} + +case "$1" in + (unlink) unlink ;; + (link|*) link ;; +esac |
