-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·220 lines (197 loc) · 6.54 KB
/
install.sh
File metadata and controls
executable file
·220 lines (197 loc) · 6.54 KB
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#!/usr/bin/env bash
set -euo pipefail
# iVim installer — symlinks ~/.vim and ~/.vimrc to this directory
if [ "$(id -u)" -eq 0 ]; then
echo "Error: Do not run this script as root or with sudo." >&2
exit 1
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TIMESTAMP="$(date +%s)"
VIM_DIR="$HOME/.vim"
VIMRC="$HOME/.vimrc"
UNDO_DIR="$HOME/.local/share/vim/undodir"
usage() {
echo "Usage: $0 [--uninstall | --help | -h]"
echo ""
echo " (no args) Install iVim (backup existing config, create symlinks)"
echo " --uninstall Remove symlinks, restore most recent backup if available"
echo " --help, -h Show this help message"
}
# Track backups made during this run so we can restore them on interrupt.
BACKUPS_MADE=()
backup_if_exists() {
local target="$1"
local expected="${2:-}"
# A target that is already the canonical iVim symlink is not user data — do
# not back it up. Otherwise a partial re-install (user removed only one of
# the two symlinks) would move the still-good link into a .bak that a later
# uninstall could wrongly "restore", orphaning the real pre-iVim config.
if [ -n "$expected" ] && [ -L "$target" ] \
&& [ "$(readlink "$target")" = "$expected" ]; then
return
fi
if [ -e "$target" ] || [ -L "$target" ]; then
local backup="${target}.bak.${TIMESTAMP}"
# Two installs within the same second would otherwise reuse the same
# backup name and silently clobber the earlier backup.
local n=1
while [ -e "$backup" ] || [ -L "$backup" ]; do
backup="${target}.bak.${TIMESTAMP}.${n}"
n=$((n + 1))
done
echo " Backing up: $target → $backup"
mv "$target" "$backup"
BACKUPS_MADE+=("$target" "$backup")
fi
}
# Restore any backups from this run — used by the signal/ERR trap so an
# interrupted install (killed between mv and ln -s) does not leave the user
# without a ~/.vim at all.
restore_on_failure() {
local exit_code=$?
# Only restore if we exited abnormally AND the install hasn't completed.
if [ "$exit_code" -ne 0 ] && [ "${INSTALL_OK:-0}" -ne 1 ]; then
local i=0
while [ "$i" -lt "${#BACKUPS_MADE[@]}" ]; do
local target="${BACKUPS_MADE[$i]}"
local backup="${BACKUPS_MADE[$((i + 1))]}"
if [ ! -e "$target" ] && [ -e "$backup" ]; then
mv "$backup" "$target" 2>/dev/null && \
echo " Rolled back: $backup → $target" >&2
fi
i=$((i + 2))
done
fi
}
trap restore_on_failure EXIT INT TERM
find_latest_backup() {
local target="$1"
# Find the most recent .bak.* file by timestamp suffix.
# Rejects non-integer suffixes (crafted filenames) and files not owned by
# the current user (TOCTOU / shared-HOME attack where a different user
# plants ~/.vim.bak.<big_ts> → hostile symlink, intending to be picked up
# on the next uninstall-and-restore).
local latest=""
local latest_ts=0
shopt -s nullglob
for f in "${target}.bak."*; do
local ts="${f##*.bak.}"
if ! [[ "$ts" =~ ^[0-9]+$ ]]; then
continue
fi
if [ ! -O "$f" ]; then
continue
fi
# Skip a "backup" that is itself a symlink into the iVim source — that is
# not the user's original config but a good iVim link a buggy older run
# may have moved aside; restoring it would re-point ~/.vim at the source.
if [ -L "$f" ]; then
local resolved
resolved="$(readlink -f "$f" 2>/dev/null || true)"
if [ "$resolved" = "$SCRIPT_DIR" ] || [ "$resolved" = "$SCRIPT_DIR/vimrc" ]; then
continue
fi
fi
if [ "$ts" -gt "$latest_ts" ]; then
latest_ts="$ts"
latest="$f"
fi
done
shopt -u nullglob
echo "$latest"
}
install() {
# If both symlinks already point here, the user has just pulled new
# commits into the existing clone and re-run install.sh. There is
# nothing to do beyond confirming so — no new backups, no relink.
if [ -L "$VIM_DIR" ] && [ "$(readlink "$VIM_DIR")" = "$SCRIPT_DIR" ] \
&& [ -L "$VIMRC" ] && [ "$(readlink "$VIMRC")" = "$SCRIPT_DIR/vimrc" ]; then
echo "iVim already installed — symlinks point at $SCRIPT_DIR."
echo "Pull the latest commits in this directory to update."
INSTALL_OK=1
return
fi
echo "Installing iVim..."
echo ""
# Backup existing config
backup_if_exists "$VIM_DIR" "$SCRIPT_DIR"
backup_if_exists "$VIMRC" "$SCRIPT_DIR/vimrc"
# Create symlinks
ln -s "$SCRIPT_DIR" "$VIM_DIR"
echo " Linked: $VIM_DIR → $SCRIPT_DIR"
ln -s "$SCRIPT_DIR/vimrc" "$VIMRC"
echo " Linked: $VIMRC → $SCRIPT_DIR/vimrc"
# Create undo directory (700 to prevent other users reading undo history).
# Idempotent: only announce when we actually had to create it.
if [ ! -d "$UNDO_DIR" ]; then
mkdir -p "$UNDO_DIR"
chmod 700 "$UNDO_DIR"
echo " Created: $UNDO_DIR"
fi
INSTALL_OK=1
echo ""
echo "iVim installed successfully!"
}
uninstall() {
echo "Uninstalling iVim..."
echo ""
# Remove symlinks (only if they point to our directory)
if [ -L "$VIM_DIR" ] && [ "$(readlink "$VIM_DIR")" = "$SCRIPT_DIR" ]; then
rm "$VIM_DIR"
echo " Removed: $VIM_DIR symlink"
local backup
backup="$(find_latest_backup "$VIM_DIR")"
if [ -n "$backup" ]; then
mv "$backup" "$VIM_DIR"
echo " Restored: $backup → $VIM_DIR"
fi
elif [ -L "$VIM_DIR" ]; then
echo " Skipped: $VIM_DIR symlink does not point to iVim"
else
echo " Skipped: $VIM_DIR is not a symlink"
fi
if [ -L "$VIMRC" ] && [ "$(readlink "$VIMRC")" = "$SCRIPT_DIR/vimrc" ]; then
rm "$VIMRC"
echo " Removed: $VIMRC symlink"
local backup
backup="$(find_latest_backup "$VIMRC")"
if [ -n "$backup" ]; then
mv "$backup" "$VIMRC"
echo " Restored: $backup → $VIMRC"
fi
elif [ -L "$VIMRC" ]; then
echo " Skipped: $VIMRC symlink does not point to iVim"
else
echo " Skipped: $VIMRC is not a symlink"
fi
# State directories created/used by iVim that may still hold user data
# (undo history, netrw history). We deliberately do NOT auto-remove
# them — destroying undo history without consent is too aggressive —
# but we point them out so the user can clean up if they want.
local data_dir="$HOME/.local/share/vim"
if [ -d "$data_dir" ]; then
echo ""
echo " Note: iVim state in $data_dir was left intact (undo history,"
echo " netrw bookmarks). Remove manually with:"
echo " rm -rf \"$data_dir\""
fi
echo ""
echo "iVim uninstalled."
}
# --- Main ---
case "${1:-}" in
--uninstall)
uninstall
;;
--help|-h)
usage
;;
"")
install
;;
*)
echo "Unknown option: $1"
usage
exit 1
;;
esac