-
Notifications
You must be signed in to change notification settings - Fork 2
/
install
executable file
·495 lines (421 loc) · 20.4 KB
/
install
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
#!/bin/bash
set -eu
# Colors
BLD=$(tput bold)
RED=$(tput setaf 1)
GRN=$(tput setaf 2)
YLW=$(tput setaf 3)
BLU=$(tput setaf 4)
PPL=$(tput setaf 5)
CYN=$(tput setaf 6)
WHT=$(tput setaf 7)
RST=$(tput sgr0)
THIS_DIR=$(cd "$(dirname "$0")" && pwd -P)
REL_DOT_DIR="git/dotfiles/config" # Replace this with repo path
INSTALL=true
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
--no-install|--link)
INSTALL=false
shift ;;
esac
done
}
linkit() {
local rel_target_path=$1
local base_link_src=$2
local src="$base_link_src/$rel_target_path"
local target=~/"$rel_target_path"
# Case where the link is set already
if [[ -h "$target" ]] && [[ $(readlink "$target") == "$src" ]]; then
printf "%-48s [${GRN}already linked${RST}]\n" "$target"
# Case where a file is present but isn't the expected link, and where we can create a backup
elif { [[ -h "$target" ]] || [[ -e "$target" ]] ; } && [[ ! -e "$target.bak" ]]; then
printf "%-48s [${YLW}wrong link - replacing link and backing up old file into %s.bak${RST}]\n" "$target" "$target"
mv "$target" "$target.bak"
ln -s "$src" "$target"
# Case where a file is present but isn't the expected link, but where a backup already exists
elif { [[ -h "$target" ]] || [[ -e "$target" ]] ; } && [[ -e "$target.bak" ]]; then
printf "%-48s [${RED}wrong link and back up already exists in %s.bak${RST} - skipping]\n" "$target" "$target"
# Case where the target doesn't exist yet (happy path)
else
printf "%-48s [${GRN}creating link${RST}]\n" "$target"
ln -s "$src" "$target"
fi
}
linkall() {
(
# We will list directories and files from there
cd "$THIS_DIR/config"
mkdir -p ~/.config
for rel_target_path in .[^.]*; do
[[ $rel_target_path == .config ]] && continue
linkit "$rel_target_path" "$REL_DOT_DIR"
done
# Create links inside ~/.config/ (the relative symlinks will start with ../)
for rel_target_path in .config/*; do
linkit "$rel_target_path" "../$REL_DOT_DIR"
done
)
}
defaults_dict_haskey() {
local domain=$1
local key=$2
local dict_key=$3
defaults read "$domain" "$key" | sed -n 's/"\{0,1\}\([^"]*\)"\{0,1\} = .*/\1/p' | grep -qF "$dict_key"
}
defaults_dict_get() {
local domain=$1
local key=$2
local dict_key=$3
# read | add key quote in case it's only one word | grep key | show key
defaults read "$domain" "$key" |
sed -n 's/^[[:space:]]*"\{0,1\}\([^"]*\)"\{0,1\} = \(.*\)/"\1" = \2/p' |
grep -F "\"$dict_key\"" |
sed -n 's/[[:space:]]*"\{0,1\}[^"]*"\{0,1\}[[:space:]]*=[[:space:]]*"\{0,1\}\([^"]*\)"\{0,1\};/\1/p' |
sed 's/\\\\/\\/g'
}
# Set the value to the macOS "defaults" dict if not set yet
defaults_dict_set_val() {
local domain=$1
local key=$2
local dict_key=$3
local dict_val=$4
if ! defaults read com.apple.universalaccess com.apple.custommenu.apps | grep -Fq "$domain"; then
defaults write com.apple.universalaccess com.apple.custommenu.apps -array-add "$domain"
fi
# Create the dict value if it doesn't exist
if ! defaults read "$domain" "$key" >/dev/null 2>&1; then
defaults write "$domain" "$key" -dict
fi
# Set the value
if ! defaults_dict_haskey "$domain" "$key" "$dict_key"; then
printf "%-32s %-32s -> [$YLW%s$RST]\n" "'$domain'" "'$key'" "added '$dict_key' --> '$dict_val'"
defaults write "$domain" "$key" -dict-add "$dict_key" "$dict_val"
return
fi
local dict_oldval
dict_oldval=$(defaults_dict_get "$domain" "$key" "$dict_key")
if [[ "$dict_oldval" != "$dict_val" ]]; then
printf "%-32s %-32s -> [$YLW%s$RST]\n" "'$domain'" "'$key'" "changed '$dict_key' from '$dict_oldval' to '$dict_val'"
defaults write "$domain" "$key" -dict-add "$dict_key" "$dict_val"
else
printf "%-32s %-32s -> [$GRN%s$RST]\n" "'$domain'" "'$key'" "'$dict_key' already set to '$dict_val'"
fi
}
# If you get:
# defaults read foo bar -> 1
# defaults read-type foo bar -> Type is integer
# Then you should set with:
# defaults_write foo bar -int 1
defaults_write() {
domain="$1"
key="$2"
val_type="$3"
val="$4"
current_val="$(defaults read "$domain" "$key" 2>/dev/null || true)"
if [[ "$val_type" == bool* ]]; then
[[ "$current_val" == 0 ]] && current_val="false"
[[ "$current_val" == 1 ]] && current_val="true"
fi
if [[ "$current_val" == "$val" ]] || [[ "$current_val" == "0" && "$val" == "false" ]]; then
printf "%-32s %-32s -> [$GRN%s$RST]\n" "'$domain'" "'$key'" "already set to '$val'"
return 0
elif [[ -z "$current_val" ]]; then
printf "%-32s %-32s -> [$YLW%s$RST]\n" "'$domain'" "'$key'" "added with value '$val'"
else
printf "%-32s %-32s -> [$YLW%s$RST]\n" "'$domain'" "'$key'" "changed from '$current_val' to '$val'"
fi
defaults write "$domain" "$key" "$val_type" "$val"
}
set_mac_prefs() {
echo "* Setting keyboard/trackpad preferences"
# Set Keyboard Shortcuts -> App Shortcuts
# To add your own, first add them in System Preferences -> Keyboard ->
# Shortcuts -> App Shortcuts, then find them in the output of:
# defaults read OR defaults find NSUserKeyEquivalents
# Use the existing and the help output of `defaults` to work it out.
# @command, ~option, ^ctrl, $shift
{
# Global ('Apple Global Domain')
defaults_dict_set_val NSGlobalDomain NSUserKeyEquivalents 'Hide Others' '@~^$7'
defaults_dict_set_val NSGlobalDomain NSUserKeyEquivalents 'Minimise' '@~^$9'
defaults_dict_set_val NSGlobalDomain NSUserKeyEquivalents 'Minimise All' '@~^$8'
defaults_dict_set_val NSGlobalDomain NSUserKeyEquivalents 'Minimize' '@~^$-'
defaults_dict_set_val NSGlobalDomain NSUserKeyEquivalents 'Minimize All' '@~^$0'
defaults_dict_set_val NSGlobalDomain NSUserKeyEquivalents 'Merge All Windows' '@~^/'
} &
{
# Keynote
defaults_dict_set_val 'com.apple.iWork.Keynote' NSUserKeyEquivalents '\033Arrange\033Align Objects\033Bottom' '@~\U2193'
defaults_dict_set_val 'com.apple.iWork.Keynote' NSUserKeyEquivalents '\033Arrange\033Align Objects\033Left' '@~\U2190'
defaults_dict_set_val 'com.apple.iWork.Keynote' NSUserKeyEquivalents '\033Arrange\033Align Objects\033Right' '@~\U2192'
defaults_dict_set_val 'com.apple.iWork.Keynote' NSUserKeyEquivalents '\033Arrange\033Align Objects\033Top' '@~\U2191'
defaults_dict_set_val 'com.apple.iWork.Keynote' NSUserKeyEquivalents 'Play Slideshow' '@~o'
} &
{
# Numbers
defaults_dict_set_val 'com.apple.iWork.Numbers' NSUserKeyEquivalents 'Delete Column' '@~$-'
defaults_dict_set_val 'com.apple.iWork.Numbers' NSUserKeyEquivalents 'Delete Columns' '@~$-'
defaults_dict_set_val 'com.apple.iWork.Numbers' NSUserKeyEquivalents 'Delete Row' '@~-'
defaults_dict_set_val 'com.apple.iWork.Numbers' NSUserKeyEquivalents 'Delete Rows' '@~-'
} &
{
# Notes
defaults_dict_set_val 'com.apple.Notes' NSUserKeyEquivalents '\033Format\033Font\033Remove Style' '@$t'
defaults_dict_set_val 'com.apple.Notes' NSUserKeyEquivalents '\033Format\033Title' '@$i'
} &
{
# Mail
defaults_dict_set_val 'com.apple.mail' NSUserKeyEquivalents '\033Message\033Mark as Read' '@^$d'
defaults_dict_set_val 'com.apple.mail' NSUserKeyEquivalents '\033Message\033Mark as Unread' '@^$d'
} &
{
# Safari
defaults_dict_set_val 'com.apple.Safari' NSUserKeyEquivalents '\033File\033Export as PDF...' '@~p'
defaults_dict_set_val 'com.apple.Safari' NSUserKeyEquivalents '\033Window\033Show Next Tab' '@~l'
defaults_dict_set_val 'com.apple.Safari' NSUserKeyEquivalents '\033Window\033Show Previous Tab' '@~h'
# defaults_dict_set_val 'com.apple.Safari' NSUserKeyEquivalents '\033Window\033Show Next Tab' '@~\U2192'
# defaults_dict_set_val 'com.apple.Safari' NSUserKeyEquivalents '\033Window\033Show Previous Tab' '@~\U2190'
defaults_dict_set_val 'com.apple.Safari' NSUserKeyEquivalents '\033Edit\033Undo Close Tab' '@$t'
} &
{
defaults_dict_set_val 'com.apple.iChat' NSUserKeyEquivalents '\033Window\033Select Next Conversation' '@~j'
defaults_dict_set_val 'com.apple.iChat' NSUserKeyEquivalents '\033Window\033Select Previous Conversation' '@~k'
} &
# Messsages - switch conversations with Cmd-option-j/k.
{
defaults_dict_set_val 'com.apple.MobileSMS' NSUserKeyEquivalents '\033Window\033Go to Next Conversation' '@~j'
defaults_dict_set_val 'com.apple.MobileSMS' NSUserKeyEquivalents '\033Window\033Go to Previous Conversation' '@~k'
} &
# Wait for defaults to be written down
wait
echo "* Setting misc Mac preferences"
# Reduce the spacing between menu bar icons to fit more of them.
defaults -currentHost write -globalDomain NSStatusItemSelectionPadding -int 6
defaults -currentHost write -globalDomain NSStatusItemSpacing -int 6
# Set up fastest key repeat rate (needs relogin).
defaults_write NSGlobalDomain KeyRepeat -int 1 &
# Sets a low time before key starts repeating.
defaults_write NSGlobalDomain InitialKeyRepeat -int 15 &
# Disable the "accent menu" when long-pressing a key.
defaults_write NSGlobalDomain ApplePressAndHoldEnabled -int 0 &
# Show Dock quickly when hidden.
defaults write com.apple.dock autohide-time-modifier -float 0.15
# Use the "scale" effect when minimising (faster than "genie").
defaults write com.apple.dock "mineffect" -string "scale"
# System Preferences -> Keyboard -> Shortcuts -> Full Keyboard Access
# Full Keyboard Access: In Windows and Dialogs, press Tab to move keyboard
# focus between:
# 0: Text Boxes and Lists only
# 2: All controls
# Set it to 2 because that's much nicer (you can close confirmation prompts
# with the keyboard, Enter to press the blue one, tab to select between them,
# space to press the Tab-selected one. If there are underlined letters, hold
# Option and press the letter to choose that option.
defaults_write NSGlobalDomain AppleKeyboardUIMode -int 2 &
defaults_write NSGlobalDomain NSAutomaticCapitalizationEnabled -bool false &
defaults_write NSGlobalDomain NSAutomaticDashSubstitutionEnabled -bool false &
defaults_write NSGlobalDomain NSAutomaticPeriodSubstitutionEnabled -bool false &
defaults_write NSGlobalDomain NSAutomaticQuoteSubstitutionEnabled -bool false &
defaults_write NSGlobalDomain NSAutomaticSpellingCorrectionEnabled -bool false &
defaults_write NSGlobalDomain NSAutomaticTextCompletionEnabled -bool false &
# Ctrl-wheel to zoom
defaults_write com.apple.AppleMultitouchTrackpad HIDScrollZoomModifierMask -int 262144 &
defaults_write com.apple.driver.AppleBluetoothMultitouch.trackpad HIDScrollZoomModifierMask -int 262144 &
defaults_write com.apple.universalaccess closeViewScrollWheelToggle -int 1 &
# defaults_write com.apple.universalaccess closeViewPanningMode -int 1 &
# Wait for defaults to be written down
wait
echo "* Setting iTerm preferences"
# Specify the preferences directory
defaults_write com.googlecode.iterm2.plist PrefsCustomFolder -string ~/.config/iTerm
# Tell iTerm2 to use the custom preferences in the directory
defaults_write com.googlecode.iterm2.plist LoadPrefsFromCustomFolder -int 1
# React quickly to mouse presence to show/hide dock (killall Dock to apply).
defaults write com.apple.dock autohide-delay -float 0
# Fast docker show/hide animation.
defaults write com.apple.dock autohide-time-modifier -float 0.12
# Remove delay in showing the draggable icon in window bars.
# You can also press down shift- to drag and drop the title.
# https://mjtsai.com/blog/2020/10/05/big-surs-hidden-document-proxy-icon/
defaults write NSGlobalDomain NSToolbarTitleViewRolloverDelay -float 0
echo "* Removing Box global shortcut"
# Removes Cmd-ctrl-alt-space shortcut.
defaults write com.box.desktop.ui DisableSearchShortcut -bool true
echo "* Setting Finder preferences"
# Default to the home directory.
defaults_write com.apple.finder NewWindowTarget -string PfHm &
defaults_write com.apple.finder NewWindowTargetPath -string "file:///Users/rchossart/" &
# Show hidden files in the finder.
if [[ "$(defaults read com.apple.finder AppleShowAllFiles)" != 1 ]]; then
defaults write com.apple.finder AppleShowAllFiles -int 1 && killall Finder
fi
# Use list view in all Finder windows by default
# Four-letter codes for the other view modes: `icnv`, `clmv`, `glyv`
defaults write com.apple.finder FXPreferredViewStyle -string "Nlsv"
# Show status bar
defaults write com.apple.Safari ShowStatusBar -bool true
defaults write com.apple.Safari ShowOverlayStatusBar -bool true
# Show the full URL in the address bar (note: this still hides the scheme)
defaults write com.apple.Safari ShowFullURLInSmartSearchField -bool true
# Safari opens with: last session
defaults write com.apple.Safari AlwaysRestoreSessionAtLaunch -bool true
# Set Safari’s home page to `about:blank` for faster loading
defaults write com.apple.Safari HomePage -string "about:blank"
# Enable the Develop menu and the Web Inspector in Safari
defaults write com.apple.Safari IncludeDevelopMenu -bool true
# Enable Safari’s debug menu
defaults write com.apple.Safari IncludeInternalDebugMenu -bool true
# Update extensions automatically
defaults write com.apple.Safari InstallExtensionUpdatesAutomatically -bool true
# Make Safari’s search banners default to Contains instead of Starts With
defaults write com.apple.Safari FindOnPageMatchesWordStartsOnly -bool false
# Website use of location services
# 0 = Deny without prompting
# 1 = Prompt for each website once each day
# 2 = Prompt for each website one time only
defaults write com.apple.Safari SafariGeolocationPermissionPolicy -int 2
# # Examples from gibfahn
#
# # Create global shortcut "Merge all windows" ⌘-M
# if ! defaults read 'Apple Global Domain' NSUserKeyEquivalents | grep -q "Merge All Windows"; then
# defaults write 'Apple Global Domain' NSUserKeyEquivalents -dict-add "Merge All Windows" '@$m'
# fi
#
# # Remove ⌘-h as a Hide Window shortcut in relevant apps.
# # -> IntelliJ Community Edition:
# if ! defaults read com.jetbrains.intellij.ce NSUserKeyEquivalents | grep -q "Hide IntelliJ IDEA"; then
# defaults write com.jetbrains.intellij.ce NSUserKeyEquivalents -dict-add "Hide IntelliJ IDEA" '@~^\\U00a7'
# fi
# # -> IntelliJ:
# if ! defaults read com.jetbrains.intellij NSUserKeyEquivalents | grep -q "Hide IntelliJ IDEA"; then
# defaults write com.jetbrains.intellij NSUserKeyEquivalents -dict-add "Hide IntelliJ IDEA" '@~^\\U00a7'
# fi
# # -> Kitty:
# if ! defaults read net.kovidgoyal.kitty NSUserKeyEquivalents | grep -q "Hide kitty"; then
# defaults write net.kovidgoyal.kitty NSUserKeyEquivalents -dict-add "Hide kitty" '~^$\\U00a7'
# fi
# # -> Mail: ⌘-backspace moves to Archive.
# if ! defaults read com.apple.mail NSUserKeyEquivalents | grep -q "Archive"; then
# defaults write com.apple.mail NSUserKeyEquivalents -dict-add "Archive" '@\U0008'
# fi
#
# # Set up fastest key repeat rate (needs relogin).
# if [[ "$(defaults read NSGlobalDomain KeyRepeat)" != 1 ]]; then
# defaults write NSGlobalDomain KeyRepeat -int 1
# fi
#
# # Sets a low time before key starts repeating.
# if [[ "$(defaults read NSGlobalDomain InitialKeyRepeat)" != 8 ]]; then
# defaults write NSGlobalDomain InitialKeyRepeat -int 8
# fi
#
# # Increases trackpad sensitivity (SysPref max 3.0).
# if [[ "$(defaults read -g com.apple.trackpad.scaling)" != 5 ]]; then
# defaults write -g com.apple.trackpad.scaling -float 5.0
# fi
#
# # Disables window minimizing animations.
# if [[ "$(defaults read NSGlobalDomain NSAutomaticWindowAnimationsEnabled)" != 0 ]]; then
# defaults write NSGlobalDomain NSAutomaticWindowAnimationsEnabled -int 0
# fi
#
# # Greys out hidden apps in the dock (so you can see which are hidden).
# if [[ "$(defaults read com.apple.Dock showhidden)" != 1 ]]; then
# defaults write com.apple.Dock showhidden -int 1 && killall Dock
# fi
#
# # System Preferences -> Keyboard -> Shortcuts -> Full Keyboard Access
# # Full Keyboard Access: In Windows and Dialogs, press Tab to move keyboard
# # focus between:
# # 0: Text Boxes and Lists only
# # 2: All controls
# # Set it to 2 because that's much nicer (you can close confirmation prompts
# # with the keyboard, Enter to press the blue one, tab to select between them,
# # space to press the Tab-selected one. If there are underlined letters, hold
# # Option and press the letter to choose that option.
# if [[ "$(defaults read -g AppleKeyboardUIMode)" != 2 ]]; then
# defaults write -g AppleKeyboardUIMode -int 2
# fi
#
# # Allow text selection in any QuickLook window.
# if [[ "$(defaults read -g QLEnableTextSelection)" != 1 ]]; then
# defaults write -g QLEnableTextSelection -int 1
# fi
#
# # Allow Finder to be quit (hides Desktop files).
# if [[ "$(defaults read com.apple.finder QuitMenuItem)" != 1 ]]; then
# defaults write com.apple.finder QuitMenuItem -bool YES
# killall Finder
# open ~
# fi
#
# # Show system info at the login screen when you click the clock icon.
# if [[ "$(defaults read /Library/Preferences/com.apple.loginwindow AdminHostInfo)" != HostName ]]; then
# sudo defaults write /Library/Preferences/com.apple.loginwindow AdminHostInfo HostName
# fi
#
# # Disable the animations for opening Quick Look windows
# if [[ "$(defaults read -g QLPanelAnimationDuration)" != 0 ]]; then
# defaults write -g QLPanelAnimationDuration -float 0
# fi
#
# # System Preferences > General > Click in the scrollbar to: Jump to the spot that's clicked
# if [[ "$(defaults read -g AppleScrollerPagingBehavior)" != 1 ]]; then
# defaults write -globalDomain AppleScrollerPagingBehavior -int 1
# fi
}
main() {
parse_args "$@"
# Config specific actions
# Note: this will unfortunately return 0 after an OS Upgrade, in which case
# "xcode-select --install" has to be run manually.
xcode-select -p >/dev/null 2>&1
if [[ $? == 2 ]]; then
echo "* Installing xcode command line tools (git, ...)"
xcode-select --install
fi
if [[ $INSTALL == true ]]; then
ln -nsf $THIS_DIR/bin ~/bin
echo "* Setting mac preferences (keyboard shortcuts) with 'defaults'"
echo "Note: you need to allow full disk access to '/bin/bash' first in Preferences"
set_mac_prefs
if ! command -v brew >/dev/null 2>&1; then
echo "* Installing brew"
echo "Go to https://brew.sh/ and manually install brew..."
echo "Once you are done, open a new shell and re-run this install script"
echo "This is needed to ensure the rest of the install script use the brew-install tools"
exit 0
fi
echo "* Installing / updating software using brew"
brew bundle install --file=$(dirname ${BASH_SOURCE[0]})/resources/Brewfile
rustup-init -y
~/.cargo/bin/rustup install stable
fi
echo "* Symlinking dotfiles"
linkall
if command -v python2 >/dev/null 2>&1; then
echo "* Adding python2 packages"
python2 -m ensurepip --user
python2 -m pip install --user --upgrade pip virtualenv neovim ropevim
fi
echo "* Adding python3 packages"
pip3 install --upgrade neovim ropevim pip
echo "* Adding ruby gems"
gem install --user-install neovim
echo "* Adding npm packages"
npm install -g neovim
echo "* Adding iCloud symlink"
ln -snf ~/'Library/Mobile Documents/com~apple~CloudDocs' ~/iCloud
echo "* Follow the following tutorial if your display sucks"
echo "https://www.mathewinkson.com/2013/03/force-rgb-mode-in-mac-os-x-to-fix-the-picture-quality-of-an-external-monitor"
echo "The script to generate the right calibration is under resources/patch-screen.rb"
if [[ $INSTALL == true ]]; then
echo "* Installing vim plugins"
nvim -c 'PlugInstall | qa'
fi
}
main "$@"
# vim: et ts=4 sw=4 sts=4