diff options
-rwxr-xr-x | bootstrap.sh | 310 |
1 files changed, 159 insertions, 151 deletions
diff --git a/bootstrap.sh b/bootstrap.sh index d37ef6a..09b8cd6 100755 --- a/bootstrap.sh +++ b/bootstrap.sh | |||
@@ -29,165 +29,173 @@ | |||
29 | ### Main entry point | 29 | ### Main entry point |
30 | 30 | ||
31 | main() { | 31 | main() { |
32 | ## Sanity checking | 32 | ## Sanity checking |
33 | # Since bootstrap.sh does some naive path-mangling, let's show an error | 33 | # Since bootstrap.sh does some naive path-mangling, let's show an error |
34 | # if it's not run correctly. Yes, there are other ways to run a | 34 | # if it's not run correctly. Yes, there are other ways to run a |
35 | # script. But this script should ideally be run, like, one time. Also | 35 | # script. But this script should ideally be run, like, one time. Also |
36 | # you can obviously comment this out or change it if you know what | 36 | # you can obviously comment this out or change it if you know what |
37 | # you're doing! | 37 | # you're doing! |
38 | 38 | ||
39 | case "$0" in | 39 | case "$0" in |
40 | ./*) ;; # this is the way bootstrap.sh /should/ be run. | 40 | ./*) ;; # this is the way bootstrap.sh /should/ be run. |
41 | *) | 41 | *) |
42 | printf >&2 'Weird invocation! %s\n' "$*" | 42 | printf >&2 'Weird invocation! %s\n' "$*" |
43 | printf >&2 'Try: cd <bootstrap-dir>; ./bootstrap.sh\n' | 43 | printf >&2 'Try: cd <bootstrap-dir>; ./bootstrap.sh\n' |
44 | exit 127 | 44 | exit 127 |
45 | ;; | 45 | ;; |
46 | esac | 46 | esac |
47 | 47 | ||
48 | ## Variables | 48 | ## Variables |
49 | 49 | ||
50 | # option: -d/--dry-run | 50 | # option: -d/--dry-run |
51 | : "${BOOTSTRAP_ACTION:=link}" | 51 | : "${BOOTSTRAP_ACTION:=link}" |
52 | # option: -v/--verbose | 52 | # option: -v/--verbose |
53 | : "${BOOTSTRAP_DEBUG:=false}" | 53 | : "${BOOTSTRAP_DEBUG:=false}" |
54 | # option: -k/--keep-going | 54 | # option: -k/--keep-going |
55 | : "${BOOTSTRAP_QUIT_ON_ERROR:=true}" | 55 | : "${BOOTSTRAP_QUIT_ON_ERROR:=true}" |
56 | # option: -m/--manifest FILE | 56 | # option: -m/--manifest FILE |
57 | : "${BOOTSTRAP_MANIFEST_FILE:=bootstrap.manifest}" | 57 | : "${BOOTSTRAP_MANIFEST_FILE:=bootstrap.manifest}" |
58 | # option: -- (rest are passed to ln) | 58 | # option: -- (rest are passed to ln) |
59 | : "${BOOTSTRAP_LN_ARGS:=-s}" | 59 | : "${BOOTSTRAP_LN_ARGS:=-s}" |
60 | 60 | ||
61 | ## Handle command-line flags | 61 | ## Handle command-line flags |
62 | # Basically an easier way of setting the above variables. | 62 | # Basically an easier way of setting the above variables. |
63 | while [ -n "$1" ]; do | 63 | while [ -n "$1" ]; do |
64 | case "$1" in | 64 | case "$1" in |
65 | -h|--help) | 65 | -h|--help) |
66 | cat >&2 <<END_HELP | 66 | cat >&2 <<END_HELP |
67 | Usage: ./bootstrap.sh [-d] [-v] [-k] [-m FILE] [-f] [-- LN_OPTS] | 67 | Usage: ./bootstrap.sh [-d] [-v] [-k] [-m FILE] [-f] [-- LN_OPTS] |
68 | OPTIONS: | 68 | OPTIONS: |
69 | -d, --dry-run | 69 | -d, --dry-run |
70 | Only print what would happen. | 70 | Only print what would happen. |
71 | -v, --verbose | 71 | -v, --verbose |
72 | Be more verbose about things. | 72 | Be more verbose about things. |
73 | -k, --keep-going | 73 | -k, --keep-going |
74 | Keep going after an error. | 74 | Keep going after an error. |
75 | -f, --force | 75 | -f, --force |
76 | Force linking. Passes -f to ln. | 76 | Force linking. Passes -f to ln. |
77 | -m FILE, --manifest FILE | 77 | -m FILE, --manifest FILE |
78 | Use FILE as manifest. | 78 | Use FILE as manifest. |
79 | Default: bootstrap.manifest. | 79 | Default: bootstrap.manifest. |
80 | -- Signify end of options. The rest are passed to ln. | 80 | -- Signify end of options. The rest are passed to ln. |
81 | END_HELP | 81 | END_HELP |
82 | exit | 82 | exit |
83 | ;; | 83 | ;; |
84 | -d|--dry-run) | 84 | -d|--dry-run) |
85 | BOOTSTRAP_ACTION=print | 85 | BOOTSTRAP_ACTION=print |
86 | shift 1 | 86 | shift 1 |
87 | ;; | 87 | ;; |
88 | -v|--verbose) | 88 | -v|--verbose) |
89 | BOOTSTRAP_DEBUG=true | 89 | BOOTSTRAP_DEBUG=true |
90 | shift 1 | 90 | shift 1 |
91 | ;; | 91 | ;; |
92 | -k|--keep-going) | 92 | -k|--keep-going) |
93 | BOOTSTRAP_QUIT_ON_ERROR=false | 93 | BOOTSTRAP_QUIT_ON_ERROR=false |
94 | shift 1 | 94 | shift 1 |
95 | ;; | 95 | ;; |
96 | -m|--manifest) | 96 | -m|--manifest) |
97 | case "$2" in | 97 | case "$2" in |
98 | ''|-*) | 98 | ''|-*) |
99 | printf >&2 "Bad argument: '$2'" | 99 | printf >&2 "Bad argument: '$2'" |
100 | exit 129 | 100 | exit 129 |
101 | ;; | 101 | ;; |
102 | esac | 102 | esac |
103 | BOOTSTRAP_MANIFEST_FILE="$2" | 103 | BOOTSTRAP_MANIFEST_FILE="$2" |
104 | shift 2 | 104 | shift 2 |
105 | ;; | 105 | ;; |
106 | -f|--force) | 106 | -f|--force) |
107 | BOOTSTRAP_LN_ARGS="$BOOTSTRAP_LN_ARGS -f" | 107 | BOOTSTRAP_LN_ARGS="$BOOTSTRAP_LN_ARGS -f" |
108 | shift 1 | 108 | shift 1 |
109 | ;; | 109 | ;; |
110 | --) | 110 | --) |
111 | BOOTSTRAP_LN_ARGS="$@" | 111 | shift 1 |
112 | break | 112 | BOOTSTRAP_LN_ARGS="$@" |
113 | ;; | 113 | break |
114 | esac | 114 | ;; |
115 | done | 115 | esac |
116 | 116 | done | |
117 | ## Main loop | 117 | |
118 | while IFS=' ' read -r source destination; do | 118 | ## Main loop |
119 | # Ignore lines beginning with '#' | 119 | while IFS=' ' read -r source destination; do |
120 | case "$source" in | 120 | # Ignore lines beginning with '#' |
121 | '#'*) | 121 | case "$source" in |
122 | if "$BOOTSTRAP_DEBUG"; then | 122 | '#'*) |
123 | printf >&2 'Skipping comment: %s %s\n' \ | 123 | if "$BOOTSTRAP_DEBUG"; then |
124 | "$source" "$destination" | 124 | printf >&2 'Skipping comment: %s %s\n' \ |
125 | fi | 125 | "$source" "$destination" |
126 | continue | 126 | fi |
127 | ;; | 127 | continue |
128 | esac | 128 | ;; |
129 | 129 | esac | |
130 | # Ignore empty lines, or lines with only SOURCE or DESTINATION | 130 | |
131 | if [ -z "$source" ] || [ -z "$destination" ]; then | 131 | # Ignore empty lines, or lines with only SOURCE or DESTINATION |
132 | if "$BOOTSTRAP_DEBUG"; then | 132 | if [ -z "$source" ] || [ -z "$destination" ]; then |
133 | printf >&2 'Skipping line: %s\t%s\n' \ | 133 | if "$BOOTSTRAP_DEBUG"; then |
134 | "$source" "$destination" | 134 | printf >&2 'Skipping line: %s\t%s\n' \ |
135 | fi | 135 | "$source" "$destination" |
136 | continue | 136 | fi |
137 | fi | 137 | continue |
138 | 138 | fi | |
139 | # Do the thing | 139 | |
140 | if ! dispatch "$source" "$destination"; then | 140 | # Do the thing |
141 | printf >&2 'ERROR: %s -> %s\n' \ | 141 | if ! dispatch "$source" "$destination"; then |
142 | "$source" "$destination" | 142 | printf >&2 'ERROR: %s -> %s\n' \ |
143 | if "$BOOTSTRAP_QUIT_ON_ERROR"; then | 143 | "$source" "$destination" |
144 | exit "$dispatch_error" | 144 | if "$BOOTSTRAP_QUIT_ON_ERROR"; then |
145 | fi | 145 | exit "$dispatch_error" |
146 | fi | 146 | fi |
147 | done < "$BOOTSTRAP_MANIFEST_FILE" | 147 | fi |
148 | done < "$BOOTSTRAP_MANIFEST_FILE" | ||
148 | } | 149 | } |
149 | 150 | ||
150 | |||
151 | ### Functions | 151 | ### Functions |
152 | 152 | ||
153 | dispatch() { # dispatch SOURCE DESTINATION | 153 | dispatch() { # dispatch SOURCE DESTINATION |
154 | # Depending on environment variables, do the linking or displaying or | 154 | # Depending on environment variables, do the linking or displaying or |
155 | # whatever of a source and a destination. | 155 | # whatever of a source and a destination. |
156 | 156 | ||
157 | ## Variables | 157 | ## Variables |
158 | 158 | ||
159 | src="$1" | 159 | src="$1" |
160 | dest="$2" | 160 | dest="$2" |
161 | dispatch_error=0 # success | 161 | dispatch_error=0 # success |
162 | 162 | ||
163 | ## Sanitize pathnames | 163 | ## Sanitize pathnames |
164 | 164 | ||
165 | # If the SOURCE starts with ~, /, or $, keep it as-is; otherwise, | 165 | # If the SOURCE starts with ~, /, or $, keep it as-is; otherwise, |
166 | # prepend "$PWD/". | 166 | # prepend "$PWD/". |
167 | case "$src" in | 167 | case "$src" in |
168 | '/'* | '~'* | '$'* ) ;; | 168 | '/'* | '~'* | '$'* ) ;; |
169 | *) src="$PWD/$src" ;; | 169 | *) src="$PWD/$src" ;; |
170 | esac | 170 | esac |
171 | 171 | ||
172 | # Convert ~ to $HOME in SOURCE and DESTINATION, to get around shell | 172 | # Convert ~ to $HOME in SOURCE and DESTINATION, to get around shell |
173 | # quoting rules. | 173 | # quoting rules. |
174 | src="$(printf '%s\n' "$src" | sed "s#^~#$HOME#")" | 174 | src="$(printf '%s\n' "$src" | sed "s#^~#$HOME#")" |
175 | dest="$(printf '%s\n' "$dest" | sed "s#^~#$HOME#")" | 175 | dest="$(printf '%s\n' "$dest" | sed "s#^~#$HOME#")" |
176 | 176 | ||
177 | ## Do the thing | 177 | ## Do the thing |
178 | 178 | ||
179 | # /Always/ tell the user what we're doing. | 179 | # /Always/ tell the user what we're doing. |
180 | printf >&2 "ln %s %s %s\n" "$BOOTSTRAP_LN_ARGS" "$src" "$dest" | 180 | if [ -f "$dest" ]; then |
181 | 181 | printf >&2 'mv %s %s.old\n' "$dest" "$dest" | |
182 | case "$BOOTSTRAP_ACTION" in | 182 | fi |
183 | link) # actually ... do the links | 183 | printf >&2 "ln %s %s %s\n" "$BOOTSTRAP_LN_ARGS" "$src" "$dest" |
184 | ln $BOOTSTRAP_LN_ARGS "$src" "$dest" || | 184 | |
185 | dispatch_error="$?" | 185 | case "$BOOTSTRAP_ACTION" in |
186 | ;; | 186 | link) # actually ... do the links |
187 | print) ;; # already printed. | 187 | # if DESTINATION exists, move it to DESTINATION.old |
188 | esac | 188 | if [ -f "$dest" ]; then |
189 | 189 | mv "$dest" "$dest.old" | |
190 | return "$dispatch_error" | 190 | fi |
191 | |||
192 | ln $BOOTSTRAP_LN_ARGS "$src" "$dest" || | ||
193 | dispatch_error="$?" | ||
194 | ;; | ||
195 | print) ;; # already printed. | ||
196 | esac | ||
197 | |||
198 | return "$dispatch_error" | ||
191 | } | 199 | } |
192 | 200 | ||
193 | ### Do the thing | 201 | ### Do the thing |