all repos — dot @ 13cc065ec014b86369dfe4ad60d1a43fa06fd251

Shell, editor and terminal configuration, and the tools that go with them

link/lib.sh (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
# shellcheck shell=bash
# shellcheck disable=SC2034  # the paths below are for whichever script sources this
#
# Shared by link.sh and the link/*.sh parts: the dry-run flag, colours, link and
# link_all, and the paths they all use. Source it with the script's own
# arguments:
#
#   source "$(dirname "$0")/lib.sh" "$@"
#
# Dry run unless the first argument is `yes`.
#
# Other repos' link.sh source this file instead of keeping copies, finding it
# through $DOT_LINK_LIB, which link.sh exports for them. Keep these names and
# the `yes` convention stable, or change them there as well.

is_dry_run="y"
if [[ "${1:-}" == "yes" ]]
then
  is_dry_run=""
fi

color() {
  color_code="$1"
  shift
  # Black        0;30  Dark Gray     1;30
  # Red          0;31  Light Red     1;31
  # Green        0;32  Light Green   1;32
  # Brown/Orange 0;33  Yellow        1;33
  # Blue         0;34  Light Blue    1;34
  # Purple       0;35  Light Purple  1;35
  # Cyan         0;36  Light Cyan    1;36
  # Light Gray   0;37  White         1;37
  yes_color="\033[${color_code}m"
  no_color='\033[0m'
  echo -e "${yes_color}$*${no_color}"
}

green() {
  color '0;32' "$@"
}

yellow() {
  color '1;33' "$@"
}

red() {
  color '0;31' "$@"
}

# Link up dotfiles
link() {
  local target="$1"
  local link="$2"
  if [[ -z "$1" ]]
  then
    >&2 echo "Error: need valid target"
    return 1
  fi
  if [[ -z "$2" ]]
  then
    >&2 echo "Error: need valid link"
    return 1
  fi
  target="$(realpath "${target}")"

  if [[ -n "${is_dry_run}" ]]
  then
    local actual
    actual="$(readlink "$link" || true)"
    if [[ -n "${actual}" ]] ; then
      actual="$(realpath "$actual" || true)"
    fi

    if [[ -L "$link" && "$target" == "$actual" ]]
    then
      green "$link already exists and is correct"
      return
    elif [[ -e "$link" ]]
    then
      if diff "$link" "$target" > /dev/null
      then
        yellow "$link already exists and will be deleted first but contents are the same. $target != $actual"
      else
        red "$link WILL CHANGE"
      fi
    else
      yellow "$link will be created"
    fi
  else
    mkdir -p "$(dirname "$2")"
    rm -rf "$2"
    ln -sf "$1" "$2"
  fi
}

link_all() {
  from="$1"
  to="$2"
  # Process substitution rather than a pipe, so `link` runs in this shell and
  # not a subshell; NUL-delimited so names with spaces survive (SC2044/SC2086).
  while IFS= read -r -d '' f
  do
    link "$f" "$to/$(basename "$f")"
  done < <(find "$from" -mindepth 1 -maxdepth 1 -print0)
}

# Run another script with this interface, dry or not as this one is.
run_link() {
  if [[ -n "${is_dry_run}" ]]
  then
    "$1"
  else
    "$1" yes
  fi
}

dotfiles_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
config_dir="$dotfiles_dir/config"
xdg_config_dir="${XDG_CONFIG_HOME:-$HOME/.config}"

# iCloud Drive, on a Mac signed into it; absent everywhere else.
icloud_dir="$HOME/Library/Mobile Documents/com~apple~CloudDocs"