-
Notifications
You must be signed in to change notification settings - Fork 0
/
install.sh
executable file
·182 lines (167 loc) · 5.61 KB
/
install.sh
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
#! /bin/bash
# dotfiles install script
# Copyright (C) 2020 xxc3nsoredxx
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# The root of the dotfiles repo
# Assumes that the script is run from the root
RUNDIR=$PWD
# Populates all the files in the repo
# Currently only looks under home/ since that's the only existing directory
CONTENTS=($(find home -depth -type f -exec realpath --relative-base=home \{\} \+))
declare -a NOT_INSTALLED
# Ensure these variables don't exist (eg, as an env var)
unset EXTRA_BLANK
unset FORCE
# Installs the file given as argument 1
# Uses hardlinks in case programs test for a file and not a symlink
# Links enable running `git pull` in the repo root to update installed files
function install_file {
printf "%-035s <<< %s\n" "$HOME/$1" "$RUNDIR/$1"
DIR_PART=$(dirname $1)
pushd $HOME >/dev/null
# Create required directories if needed
mkdir -p $DIR_PART
cd $DIR_PART
if [[ ! -z $FORCE ]]; then
ln -f $RUNDIR/home/$1
else
ln $RUNDIR/home/$1
fi
popd >/dev/null
}
# Print the usage string
# Triggered by '-h' or invalid argument
function usage {
echo "Usage: $0 [args]"
echo "Args:"
echo " -f Force"
echo " Overwrites existing files."
echo " -h Display this help"
echo " -l Display license info"
exit 1
}
# Test the commandline arguments
if [ $# -ge 1 ]; then
if [ "$1" == "-f" ]; then
echo "WARNING: '-f' given. Will mercilessly overwrite existing files."
echo -n "Continuing in: 5"
sleep 1
echo -n " 4"
sleep 1
echo -n " 3"
sleep 1
echo -n " 2"
sleep 1
echo -n " 1"
sleep 1
echo " Now!"
FORCE=1
elif [ "$1" == "-l" ]; then
sed -nEe '3,+14 {s/^# *//; p}' $0
exit
else
usage
fi
fi
# Find the files which are not installed
# Notifies if existing files differ from the repo files (unless -f)
for i in ${CONTENTS[@]}; do
if [[ ! -a $HOME/$i ]]; then
NOT_INSTALLED[${#NOT_INSTALLED[@]}]=$i
elif [[ ! -z $FORCE ]]; then
NOT_INSTALLED[${#NOT_INSTALLED[@]}]=$i
EXTRA_BLANK=1
elif [[ ! -z $(diff -q $HOME/$i home/$i) ]]; then
echo "Contents of '$HOME/$i' differ from the repo..."
EXTRA_BLANK=1
fi
done
if [ ! -z $EXTRA_BLANK ]; then
echo ""
fi
# Check if any files need to be installed
if [[ ${#NOT_INSTALLED[@]} -eq 0 ]]; then
echo "Nothing to install, exiting..."
exit
fi
# Installer menu
echo "Separate choices by space (' ') to install multiple files at once."
echo "NOTE: 'all' is mutually exclusive with everything."
echo " Other choices take precedence over 'all'."
while true; do
# Output choices
COUNT=1
for NAME in ${NOT_INSTALLED[@]} all exit; do
echo "${COUNT}) $NAME"
COUNT=$(($COUNT + 1))
done
# Read into an array to enable multi-choice
echo -n "Install: "
readarray -n 1 INPUT
# Sort and remove duplicate entries
INPUT=($(echo "${INPUT[@]}" | tr -s ' ' '\n' | sort -n | uniq))
# These values depend on the state before any files are installed
# Number of not installed files
N_NOT_INSTALLED=${#NOT_INSTALLED[@]}
# The array can (and probably will) have holes, need to index using the
# array of valid indices
INDICES=(${!NOT_INSTALLED[@]})
# Parse each choice from the input
for C in ${INPUT[@]}; do
# Remove non-numeric characters
CHOICE=${C//[^0-9]}
# Non-numeric choice
if [ "$CHOICE" != "$C" ]; then
echo "Invalid choice: '$C', ignoring..."
continue
# Out of bounds choices
elif [ $CHOICE -eq 0 ]; then
echo "Invalid choice: '$CHOICE', ignoring..."
continue
elif [ $CHOICE -gt $(($N_NOT_INSTALLED + 2)) ]; then
echo "Invalid choice: '$CHOICE', ignoring..."
continue
fi
# Turn into a proper array index
CHOICE=$(($CHOICE - 1))
# Test if "all" was selected
if [[ $CHOICE -eq $N_NOT_INSTALLED ]]; then
# Install all the files only if "all" is the only choice
if [ ${#INPUT[@]} -eq 1 ]; then
echo "Installing all..."
for FILE in ${NOT_INSTALLED[@]}; do
install_file $FILE
done
unset NOT_INSTALLED
else
echo "Invalid choice in this context: 'all', ignoring..."
fi
# Test if "exit" was selected
# Always the last possible choice so it's safe to exit if given
elif [[ $CHOICE -eq $(($N_NOT_INSTALLED + 1)) ]]; then
echo "Exit..."
exit
# Install a single file
else
install_file ${NOT_INSTALLED[${INDICES[$CHOICE]}]}
unset NOT_INSTALLED[${INDICES[$CHOICE]}]
fi
done
# Check if no more files left
if [[ ${#NOT_INSTALLED[@]} -eq 0 ]]; then
echo "Nothing left to install, exiting..."
exit
fi
done