| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 | 
							- #!/bin/bash
 - 
 - warn() {
 -     echo "$@" >&2
 - }
 - 
 - think() {
 -     $Verbose || return
 -     warn "$@"
 - }
 - 
 - die() {
 -     warn "$@"
 -     exit 3
 - }
 - 
 - usage() {
 -     warn "usage: $0 init     VAULT SUBVAULT"
 -     warn "usage: $0 explore  VAULT SUBVAULT"
 -     exit 2
 - }
 - 
 - debug() {
 -     $Debug || return
 -     local msg
 -     for msg in "$@";
 -     do
 -         warn "debug:$msg"
 -     done
 - }
 - 
 - debugv() {
 -     $Debug || return
 -     local vn
 -     local vd
 -     for vn in "$@";
 -     do
 -         if vd=$(declare -p "$vn" 2>/dev/null);
 -         then
 -             warn "debug:${vd#declare ?? }"
 -         else
 -             warn "debug:$vn #Unset"
 -         fi
 -     done
 - }
 - 
 - debugc() {
 -     $Debug || return
 -     debug "-- cmd begin: $*"
 -     Debug=false "$@" | sed -e 's/^/debug:/' >&2
 -     debug "-- cmd end --"
 - }
 - 
 - lssv() {
 -     #
 -     # List sub-vaults
 -     #
 -     # Sub-vault is a sub-path of vault directory that contains items
 -     # that should be linked separately, to a similar $HOME subpath,
 -     # rather than linking the whole sub-tree.  This is typical for
 -     # .config directory specified by XDG standard.
 -     #
 -     # For example, imagine vault like this:
 -     #
 -     #     dotfiles/
 -     #     ├── config
 -     #     │   ├── dunst
 -     #     │   │   └── dunstrc
 -     #     │   └── uzbl
 -     #     │       ├── config
 -     #     │       └── style.css
 -     #     ├── i3
 -     #     │   └── config
 -     #     ├── i3status.conf
 -     #     └── vimrc
 -     #
 -     # Without sub-vault, the "config" would be linked as "$HOME/.config":
 -     #
 -     #     $HOME/.config         -> dotfiles/.config
 -     #     $HOME/.i3             -> dotfiles/i3
 -     #     $HOME/.i3status.conf  -> dotfiles/i3status.conf
 -     #     $HOME/.vimrc          -> dotfiles/vimrc
 -     #
 -     # This is impractical in most cases, as normally you want to select
 -     # applications to link--some dotfiles could be specific to some
 -     # hosts or even private.
 -     #
 -     # Setting 'config' as sub-vault will tell mklinks that you don't
 -     # want to link this item directly but rather each of its items:
 -     #
 -     #     $HOME/.config/dunst   -> dotfiles/config/dunst
 -     #     $HOME/.config/uzbl    -> dotfiles/config/uzbl
 -     #     $HOME/.i3             -> dotfiles/i3
 -     #     $HOME/.i3status.conf  -> dotfiles/i3status.conf
 -     #     $HOME/.vimrc          -> dotfiles/vimrc
 -     #
 -     # This way, you can have other items under "$HOME/.config" that
 -     # are either purely local or linked to another vault.
 -     #
 -     # Note that the subvault can be deeper than one level, i.e.
 -     # subvault "local/share" also works:
 -     #
 -     #     $HOME/.local/share/klavaro -> dotfiles/local/share/klavaro
 -     #
 -     local sv
 -     test -n "${SubVaults[0]}" && {
 -         think "using sub-vaults from command line"
 -         for sv in "${SubVaults[@]}";
 -         do
 -             echo "$sv"
 -         done
 -         return
 -     }
 -     test -f "$Vault/dotvault/subvaults" && {
 -         think "reading subvaults from file: $Vault/dotvault/subvaults"
 -         grep . "$Vault/dotvault/subvaults" \
 -           | grep -v "^#.*"
 -         return
 -     }
 -     think "using hard-coded list of subvaults"
 -     echo config
 - }
 - 
 - lsvault() {
 -     #
 -     # List all vaults and subvaults, depth-first
 -     #
 -     {
 -         echo "$Vault"
 -         lssv \
 -           | while IFS= read -r sv;
 -             do
 -                 test -e "$Vault/$sv" || {
 -                     think "ignoring non-existent sub-vault: $sv"
 -                     continue
 -                 }
 -                 test -d "$Vault/$sv" || {
 -                     warn "ignoring non-directory sub-vault: $sv"
 -                     continue
 -                 }
 -                 echo "$Vault/$sv"
 -             done
 -     } \
 -       | LC_ALL=C sort \
 -       | tac
 - }
 - 
 - istarget() {
 -     #
 -     # True if item $1 is possible target
 -     #
 -     # Item is obviously not a possible target if it's
 -     # a (sub-)vault.
 -     #
 -     # A less obvious case is when item is an intermediate directory
 -     # on a path leading to a sub-vault.  Such item cannot be a target!
 -     #
 -     # For example: there are sub-vaults:
 -     #
 -     #     foo
 -     #     foo/bar/baz
 -     #
 -     # and a tree:
 -     #
 -     #     foo
 -     #     ├── A
 -     #     └── bar
 -     #         └── baz
 -     #             └── B
 -     #
 -     # Items A and B could both be targets, but baz could not be!
 -     #
 -     #TODO: write an explanation
 -     #
 -     local item=$1
 -     debugv item
 -     Verbose=false lsvault | grep -qxF "$item"   && return 1
 -     Verbose=false lsvault | grep -qx "$item/.*" && return 1
 -     return 0
 - }
 - 
 - pfxpath() {
 -     #
 -     # Prefix paths with $1
 -     #
 -     # Just like `sed "s/^/$1/" but works with any characters
 -     # in $1 excepr newline, which is not allowed in paths..
 -     #
 -     local pfx="$1"
 -     local path
 -     while IFS= read -r path;
 -     do
 -         echo "$pfx$path"
 -     done
 - }
 - 
 - lstarget() {
 -     #
 -     # List potential items in vault $1
 -     #
 -     local vlt=$1
 -     debugv vlt
 -     local item
 -     find "$vlt" -mindepth 1 -maxdepth 1 -printf "%P\n" \
 -       | pfxpath "$vlt/" \
 -       | grep -v "$Vault/dotvault" \
 -       | while IFS= read -r item;
 -         do
 -             istarget "$item" && echo "$item"
 -         done
 - }
 - 
 - make_links() {
 -     #
 -     # Create all links
 -     #
 -     local slpath
 -     local target
 -     local vlt
 -     lsvault \
 -       | while IFS= read -r vlt;
 -         do
 -             lstarget "$vlt" \
 -               | while IFS= read -r target;
 -                 do
 -                     slpath=$(slpath "$target")
 -                     if test -L "$slpath";
 -                     then
 -                         $ClobberLinks || {
 -                             warn "skipping existing slpath: $slpath"
 -                             continue
 -                         }
 -                         think "removing existing link: $slpath"
 -                         maybe rm "$slpath"
 -                     fi
 -                     test -e "$slpath" && {
 -                         warn "skipping existing non-link: $slpath"
 -                         continue
 -                     }
 -                     maybe ln -sr "$target" "$slpath"
 -                 done
 -         done
 - }
 - 
 - explore() {
 -     #
 -     # Show what would be done
 -     #
 -     local vlt
 -     local tgt
 -     local tgts
 -     lsvault \
 -       | sort \
 -       | while IFS= read -r vlt;
 -         do
 -             tgts=$(lstarget "$vlt")
 -             test -n "$tgts" || continue
 -             if test "$vlt" == "$Vault";
 -             then
 -                 echo "VAULT:$vlt:"
 -             else
 -                 echo
 -                 echo "SUBVAULT:$vlt"
 -             fi
 -             sort <<<"$tgts" \
 -               | while IFS= read -r tgt;
 -                 do
 -                     echo "${tgt}:$(slpath "$tgt")"
 -                 done \
 -               | sed -e "s|:$HOME|:=> ~|" \
 -               | column -ts: \
 -               | sed -e "s/^/    /"
 -         done
 - }
 - 
 - maybe() {
 -     #
 -     # Maybe do things $@, maybe not (if in debug mode)
 -     #
 -     $Debug && { debug "WOULD: $*"; return; }
 -     "$@"
 - }
 - 
 - slpath() {
 -     #
 -     # Print path where to create link for target $1
 -     #
 -     local item=$1
 -     echo "$HOME/.${item#$Vault/}"
 - }
 - 
 - route() {
 -     local Vault=$1; shift
 -     local SubVaults=()
 -     test -n "$Vault" || usage
 -     test -e "$Vault" || die "vault does not exist: $Vault"
 -     test -d "$Vault" || die "vault is not a directory: $Vault"
 -     SubVaults=("$@")
 -     case $Action in
 -         init)       make_links  ;;
 -         explore)    explore     ;;
 -         *)          usage       ;;
 -     esac
 - }
 - 
 - export LC_ALL=C
 - 
 - main() {
 -     local Action
 -     local Debug=false
 -     local Verbose=false
 -     local ClobberLinks=false
 -     while true; do case $1 in
 -         -d) Debug=true;         shift ;;
 -         -v) Verbose=true;       shift ;;
 -         -f) ClobberLinks=true;  shift ;;
 -         -*)                     usage ;;
 -         *)                      break ;;
 -     esac done
 -     Action=$1; shift
 -     route "$@"
 - }
 - 
 - main "$@"
 
 
  |