summaryrefslogtreecommitdiff
path: root/link/lib.sh
blob: 1899f00c0f06918bfa601c8ccd5e740fee2d998b (plain)
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"