| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 | #!/bin/bash
shellfu import pretty
#
# Should broken links be silently overwritten?
#
DOTTUM__CLOBBER_BROKEN=false
#
# Should all links be silently overwritten?
#
DOTTUM__CLOBBER_LINKS=false
#
# List of subvalults passed on command line
#
# This is added to the front of list in *subvalults* file.
#
DOTTUM__SUBVAULTS=()
#
# Vault to process
#
DOTTUM__VAULT=""
_dottum__slstatus() {
    #
    # Print status of slpath $1
    #
    local slpath=$1
    local tlink=0
    local texis=0
    test -L "$slpath"; tlink=$?
    test -e "$slpath"; texis=$?
    case $tlink:$texis in
        0:0)  echo symlink ;;
        0:1)  echo broken ;;
        1:0)  echo nonlink ;;
        1:1)  echo nothing ;;
        *) warn "strange resiult: tlink=$tlink texis=$texis" ;;
    esac
}
dottum__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 "${DOTTUM__SUBVAULTS[0]}" && {
        think "using sub-vaults from command line"
        for sv in "${DOTTUM__SUBVAULTS[@]}";
        do
            echo "$sv"
        done
        return
    }
    test -f "$DOTTUM__VAULT/dotvault/subvaults" && {
        think "reading subvaults from file: $DOTTUM__VAULT/dotvault/subvaults"
        grep . "$DOTTUM__VAULT/dotvault/subvaults" \
          | grep -v "^#.*"
        return
    }
    think "using hard-coded list of subvaults"
    echo config
}
dottum__lsvault() {
    #
    # List all vaults and subvaults, depth-first
    #
    {
        echo "$DOTTUM__VAULT"
        dottum__lssv \
          | while IFS= read -r sv;
            do
                test -e "$DOTTUM__VAULT/$sv" || {
                    think "ignoring non-existent sub-vault: $sv"
                    continue
                }
                test -d "$DOTTUM__VAULT/$sv" || {
                    warn "ignoring non-directory sub-vault: $sv"
                    continue
                }
                echo "$DOTTUM__VAULT/$sv"
            done
    } \
      | LC_ALL=C sort \
      | tac
}
dottum__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
    debug -v item
    Verbose=false dottum__lsvault | grep -qxF "$item"   && return 1
    Verbose=false dottum__lsvault | grep -qx "$item/.*" && return 1
    return 0
}
dottum__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
}
dottum__lstarget() {
    #
    # List potential items in vault $1
    #
    local vlt=$1
    debug -v vlt
    local item
    find "$vlt" -mindepth 1 -maxdepth 1 -printf "%P\n" \
      | dottum__pfxpath "$vlt/" \
      | grep -v "$DOTTUM__VAULT/dotvault" \
      | while IFS= read -r item;
        do
            dottum__istarget "$item" && echo "$item"
        done
}
dottum__make_links() {
    #
    # Create all links
    #
    local slpath
    local target
    local vlt
    local slstatus
    dottum__lsvault \
      | while IFS= read -r vlt;
        do
            dottum__lstarget "$vlt" \
              | while IFS= read -r target;
                do
                    slpath=$(dottum__slpath "$target")
                    slstatus=$(_dottum__slstatus "$slpath")
                    debug -v slpath slstatus
                    case $slstatus in
                        nonlink)
                            warn "skipping existing non-link: $slpath"
                            continue
                            ;;
                        broken)
                            if $DOTTUM__CLOBBER_LINKS || $DOTTUM__CLOBBER_BROKEN; then
                                think "removing existing broken link: $slpath"
                                dottum__maybe rm "$slpath"
                            else
                                warn "skipping existing broken link: $slpath"
                                continue
                            fi
                            ;;
                        symlink)
                            if $DOTTUM__CLOBBER_LINKS; then
                                think "removing existing link: $slpath"
                                dottum__maybe rm "$slpath"
                            else
                                warn "skipping existing slpath: $slpath"
                                continue
                            fi
                            ;;
                    esac
                    dottum__maybe ln -sr "$target" "$slpath"
                done
        done
}
dottum__explore() {
    #
    # Show what would be done
    #
    local vlt
    local tgt
    local tgts
    dottum__lsvault \
      | sort \
      | while IFS= read -r vlt;
        do
            tgts=$(dottum__lstarget "$vlt")
            test -n "$tgts" || continue
            if test "$vlt" == "$DOTTUM__VAULT";
            then
                echo "VAULT:$vlt:"
            else
                echo
                echo "SUBVAULT:$vlt"
            fi
            sort <<<"$tgts" \
              | while IFS= read -r tgt;
                do
                    echo "${tgt}:$(dottum__slpath "$tgt")"
                done \
              | sed -e "s|:$HOME|:=> ~|" \
              | column -ts: \
              | sed -e "s/^/    /"
        done
}
dottum__maybe() {
    #
    # Maybe do things $@, maybe not (if in debug mode)
    #
    $PRETTY_DEBUG && { debug "MAYBE:$*"; return; }
    "$@"
}
dottum__slpath() {
    #
    # Print path where to create link for target $1
    #
    local item=$1
    echo "$HOME/.${item#$DOTTUM__VAULT/}"
}
 |