-
Notifications
You must be signed in to change notification settings - Fork 2
/
conman
executable file
·286 lines (234 loc) · 8.21 KB
/
conman
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
#!/usr/bin/env bash
################################################################################
# Argument Parsing Utilities
################################################################################
# Stores a map from argument -> action, where action is executed with eval.
typeset -A arguments
typeset -A arguments_help
# Add an argument to the program. Args:
# - Argument (e.g. -f)
# - Command to execute when present
# - Help string
# In the command of the argument, $1 is the next argument, and should be shifted
# if used.
function add_argument() {
arguments["$1"]="$2"
arguments_help["$1"]="$3"
}
# Adds an argument which sets an environment variable. Args:
# - Argument
# - Variable name
# - Help string (which is appended with '(Default = ...)'
function add_argument_var() {
local CUR_VAL="$(eval "echo \$$2")"
[ -n "$CUR_VAL" ] && local DEFAULT_NOTICE=" (Default = $CUR_VAL)"
add_argument "$1" "$2=\"\$1\";shift" "$3$DEFAULT_NOTICE"
}
# Adds an argument which sets an environment variable. Args:
# - Argument
# - Variable name
# - Help string (which is appended with '(Default = ...)'
function add_argument_path_var() {
local CUR_VAL="$(eval "echo \$$2")"
[ -n "$CUR_VAL" ] && local DEFAULT_NOTICE=" (Default = $CUR_VAL)"
add_argument "$1" "$2=\"\$1\";shift" "$3$DEFAULT_NOTICE"
}
# Print the help strings for all arguments.
function print_help() {
echo "Usage: $0 [OPTION]"
echo
for option in "${!arguments_help[@]}"; do
printf " %-10s %s\n" $option "${arguments_help[$option]}"
done
echo
}
add_argument -h "print_help; exit 1" "Display this message."
################################################################################
# Constants & File/Dir Definitions
################################################################################
# Directory ConMan is installed in
CONMAN_DIR="$(readlink -e "$(dirname "$0")")"
# The location that is scanned for m4 files to use for configuration
CONFIG_DIR="$CONMAN_DIR/configs"
add_argument_var -t "CONFIG_DIR" "Override the config template directory."
# The location of the local configuration file.
LOCAL_CONFIG_FILE="$CONMAN_DIR/local_config.m4"
add_argument_var -c "LOCAL_CONFIG_FILE" "Set the local config file location."
# The location of the local configuration file.
GLOBAL_CONFIG_FILE="$CONMAN_DIR/config.m4"
add_argument_var -C "GLOBAL_CONFIG_FILE" "Set the global config file location."
# Location where plugins for ConMan live.
PLUGIN_DIR="$CONMAN_DIR/plugins"
add_argument_var -p "PLUGIN_DIR" "Override ConMan plugin directory."
# Location where backups of installed scripts are kept
BACKUP_DIR="$CONMAN_DIR/backups"
add_argument_var -b "BACKUP_DIR" "Override backup file directory."
# Bool specifying whether files should be overwritten without confirmation
FORCE_OVERWRITE=0
add_argument -f "FORCE_OVERWRITE=1" "Force all files to be updated."
# Bool specifying whether to start editing the named config file
EDIT_FILE=""
add_argument_var -e "EDIT_FILE" "Edit the m4 template for a file $EDITOR."
# A file which is populated during the first pass with commands to execute
# during the second
SCRIPT_FILE="$(mktemp)"
# A file which is included at the end of the header and which can be used by
# plugins to extend the M4 interface.
M4_INCLUDE="$(mktemp --suffix=.m4)"
# A tempoary file where config files will get dumped before being written to the
# system
OUT_FILE="$(mktemp)"
################################################################################
# Utilities for Plugins
################################################################################
# Adds an M4 command for the required action. Args:
# - M4 command name
# - Bash function to execute
function add_m4_command() {
echo "define($1, \`ADD_TO_SCRIPT($2 SCRIPT_ARGS(\$@))dnl')" \
>> "$M4_INCLUDE"
}
################################################################################
# General M4 Wrapper
################################################################################
# Call M4 with the conventional arguments for M4 as used in ConMan. Args:
# - m4_file target
# - script_file to write to
# - id whose output to print
# - [all other arguments appended to end]
function conman_m4() {
m4_file="$1"
script_file="$2"
id="$3"
shift 3
m4 \
-DM4_FILE="$m4_file" \
-DSCRIPT_FILE="$script_file" \
-DOUTPUT_ID="$id" \
-I"$CONFIG_DIR" \
"$CONMAN_DIR/header.m4" \
"$M4_INCLUDE" \
"$LOCAL_CONFIG_FILE" \
"$GLOBAL_CONFIG_FILE" \
"${M4_PLUGINS[@]}" \
"$m4_file" \
"$@"
}
################################################################################
# File Installation Functions
################################################################################
# Install the file in $1 to $2. (Essentially a copy with warnings!)
# This keeps backups and warns if the destination file has changed
# since this was last ran.
function install_file() {
src="$1"
dest="$2"
backup="$BACKUP_DIR/$dest"
# Check that we're not about to clober anything (unless FORCE_OVERWRITE is 1)
# If the destination and the backup exists, check the diff.
CONFIRM="0"
if [ "$FORCE_OVERWRITE" != "1" ]; then
if [ -f "$dest" ]; then
if [ -f "$backup" ]; then
if ! diff -c "$backup" "$dest"; then
echo "WARNING: $dest changed since last run."
CONFIRM="1"
fi
else
diff -c "$dest" "$src"
echo "WARNING: $dest not generated by this script."
CONFIRM="1"
fi
fi
fi
# Confirm continue if overwrite not specified
if [ "$CONFIRM" == "1" ]; then
read -p "Overwrite it anyway? (y/N) " response
[ "$response" != "y" ] && return 1
fi
# Perform the install.
mkdir -p "$(dirname "$backup")" "$(dirname "$dest")"
cp "$src" "$dest"
cp "$src" "$backup"
}
# Generate the config file with id $1, name $2, and install it in $3.
function config_file() {
id="$1"
name="$2"
path="$3"
m4_file="$4"
# Generate the config file.
conman_m4 "$m4_file" "/dev/null" "$id" > "$OUT_FILE"
# ...and install it.
install_file "$OUT_FILE" "$path"
}
################################################################################
# File Editing Functions
################################################################################
# Try to edit the template for a file given the scriptfile
# 1: source filename
# 2: template name
function edit_template {
EDIT_FILE="$1"
SCRIPT_FILE="$2"
M4_FILE="$(
# Redefine here to edit if the filename matches
function config_file() {
FILE="$3"
M4_FILE="$4"
if [ "$(readlink -e "$FILE")" == "$(readlink -e "$EDIT_FILE")" ]; then
echo "$M4_FILE"
fi
}
# Test every config_file file in the script
eval "$(grep -E "^config_file" "$SCRIPT_FILE")"
)"
if [ -n "$M4_FILE" ]; then
${EDITOR:-vim} "$M4_FILE"
return $?
else
echo "This file was not created by $0!" >> /dev/stderr
return 1
fi
}
################################################################################
# Executable (World Starts Here)
################################################################################
# Clear the script/m4 include file
echo -n > "$SCRIPT_FILE"
echo -n > "$M4_INCLUDE"
# Give us the fancy double-star glob
shopt -s globstar
# Find the script plugins
SH_PLUGINS=("$PLUGIN_DIR"/**/*.sh)
# Makes less mess if no plugins found
[ -f "${SH_PLUGINS[0]}" ] || SH_PLUGINS="/dev/null"
# Source all scripts in the scripts directory.
for script in "${SH_PLUGINS[@]}" ; do
[ -x "$script" ] && source $script
done
# Find the m4 plugins to include
M4_PLUGINS=("$PLUGIN_DIR"/**/*.m4)
# Makes less mess if no plugins found
[ -f "${M4_PLUGINS[0]}" ] || M4_PLUGINS="/dev/null"
# Parse all command line args (including those defined by scripts).
while (($#)); do
arg=$1
shift
eval "${arguments[$arg]}"
done
# Process all m4 files that aren't '*.inc.m4'
find "$CONFIG_DIR" -name '*.m4' '!' -name '*.inc.m4' | while read m4_file; do
# Generate and run the corresponding script.
conman_m4 "$m4_file" "$SCRIPT_FILE" "" || continue
done
# Either update everything or attempt to edit the specified file
if [ -n "$EDIT_FILE" ]; then
edit_template "$EDIT_FILE" "$SCRIPT_FILE"
exit $?
else
# Execute the commands required by all the m4 files.
source "$SCRIPT_FILE"
fi
# Clean up
rm "$OUT_FILE" "$SCRIPT_FILE" "$M4_INCLUDE"