|
@@ -1,410 +0,0 @@
|
1
|
|
-#!/bin/bash
|
2
|
|
-
|
3
|
|
-shellfu import exit
|
4
|
|
-shellfu import inigrep
|
5
|
|
-shellfu import pretty
|
6
|
|
-
|
7
|
|
-#
|
8
|
|
-# Path where saturnin__conf should look for files
|
9
|
|
-#
|
10
|
|
-# If filename does not contain slash, it is looked up in each
|
11
|
|
-# (or all, based on strategy--see saturnin__conf() doc) path in this
|
12
|
|
-# list. The list is colon-separated and non-dirs as well as
|
13
|
|
-# empty strings are silently ignored.
|
14
|
|
-#
|
15
|
|
-SATURNIN_CONF_PATH="${SATURNIN_CONF_PATH:-}"
|
16
|
|
-
|
17
|
|
-#
|
18
|
|
-# Expected config filename extension (for guessing from path head)
|
19
|
|
-#
|
20
|
|
-# If no filename to read is given, saturnin__conf() will guess
|
21
|
|
-# filename as the path head plus this suffix (e.g. `foo.ini` for
|
22
|
|
-# `saturnin__conf foo.bar.baz`)
|
23
|
|
-#
|
24
|
|
-SATURNIN_CONF_SUFFIX="${SATURNIN_CONF_SUFFIX:-.ini}"
|
25
|
|
-
|
26
|
|
-saturnin__conf() {
|
27
|
|
- #
|
28
|
|
- # inigrep smart loader
|
29
|
|
- #
|
30
|
|
- # Usage:
|
31
|
|
- # saturnin__conf [-j] [inigrep-query] [-- [file]..]
|
32
|
|
- #
|
33
|
|
- #
|
34
|
|
- # File arguments
|
35
|
|
- # ==============
|
36
|
|
- #
|
37
|
|
- # If omitted, *file* argument is inferred by taking part of
|
38
|
|
- # kpath name before first dot and appending value of
|
39
|
|
- # `$SATURNIN_CONF_SUFFIX`, (".ini" by default).
|
40
|
|
- #
|
41
|
|
- # Each *file* argument is then processed as follows:
|
42
|
|
- #
|
43
|
|
- # * `-` (single dash) is interpreted as reading from
|
44
|
|
- # STDIN.
|
45
|
|
- #
|
46
|
|
- # * If argument contains slash, it is expanded as a regular
|
47
|
|
- # path (relative or absolute).
|
48
|
|
- #
|
49
|
|
- # * Otherwise, it is taken as filename and searched for
|
50
|
|
- # in directories given in `$SATURNIN_CONF_PATH`. (This can
|
51
|
|
- # yield more than one path, which is equivalent as if
|
52
|
|
- # all paths were provided.)
|
53
|
|
- #
|
54
|
|
- # Not all files expanded based on `$SATURNIN_CONF_PATH`
|
55
|
|
- # are read by default; reading is governed by "merge
|
56
|
|
- # strategy": the default strategy "first" reads only
|
57
|
|
- # the first existing file.
|
58
|
|
- #
|
59
|
|
- # "join" strategy on the other hand, means that any
|
60
|
|
- # files are simply concatenated and prefixed with
|
61
|
|
- # comment (visible only in raw mode) containing path
|
62
|
|
- # to the file.
|
63
|
|
- #
|
64
|
|
- # This means that if a section is queried that is
|
65
|
|
- # present in both files, it is effectively concatenated
|
66
|
|
- # as well.
|
67
|
|
- #
|
68
|
|
- # Following calls are equivalent
|
69
|
|
- #
|
70
|
|
- # saturnin__conf foo.bar.baz
|
71
|
|
- # saturnin__conf foo.bar.baz foo.ini
|
72
|
|
- #
|
73
|
|
- # and both result in reading of key *baz* from section *foo.bar*
|
74
|
|
- # in file *foo.ini*, which is selected from *SATURNIN_CONF_PATH*.
|
75
|
|
- # Should there be more foo.ini's, the first is selected.
|
76
|
|
- # Using `-j` switch
|
77
|
|
- #
|
78
|
|
- # saturnin__conf -j foo.bar.baz
|
79
|
|
- #
|
80
|
|
- # would cause all foo.ini's on *SATURNIN_CONF_PATH* be
|
81
|
|
- # concatenated instead.
|
82
|
|
- #
|
83
|
|
- local ig_mode # retrieval mode
|
84
|
|
- local ig_query # keypath or section name (when listing keys)
|
85
|
|
- local ig_limit # line limit
|
86
|
|
- local files=() # file specification
|
87
|
|
- local Strategy=first # merge strategy
|
88
|
|
- while true; do case $1:$2 in
|
89
|
|
- "":*) break ;;
|
90
|
|
- -j:*) Strategy=join; shift 1 ;;
|
91
|
|
- -1:*) ig_limit=$1; shift 1 ;;
|
92
|
|
- -e:*.*) ig_mode=$1; ig_query=$2; shift 2; break ;;
|
93
|
|
- -r:*.*) ig_mode=$1; ig_query=$2; shift 2; break ;;
|
94
|
|
- -K:*) ig_mode=$1; ig_query=$2; shift 2; break ;;
|
95
|
|
- -S:*) ig_mode=$1; ig_query=""; shift 1; break ;;
|
96
|
|
- -P:*) ig_mode=$1; ig_query=""; shift 1; break ;;
|
97
|
|
- .*:*) warn "bad syntax: $*"; _saturnin__conf_usage ;;
|
98
|
|
- *.*:*) ig_mode=-e; ig_query=$1; shift 1; break ;;
|
99
|
|
- --help:*) _saturnin__conf_usage -e 0 ;;
|
100
|
|
- *) warn "bad syntax: $*"; _saturnin__conf_usage ;;
|
101
|
|
- esac done
|
102
|
|
- test -n "$ig_mode" || { warn "could not determine inigrep mode"; _saturnin__conf_usage; }
|
103
|
|
- debug -v ig_limit ig_query ig_mode Strategy
|
104
|
|
- if test -n "$*";
|
105
|
|
- then
|
106
|
|
- files=("$@")
|
107
|
|
- elif test -n "$ig_query";
|
108
|
|
- then
|
109
|
|
- files=("${ig_query%%.*}$SATURNIN_CONF_SUFFIX")
|
110
|
|
- else
|
111
|
|
- warn "dunno what to load"
|
112
|
|
- _saturnin__conf_usage
|
113
|
|
- fi
|
114
|
|
- debug -v files
|
115
|
|
- #shellcheck disable=SC2086
|
116
|
|
- _saturnin__conf__load "${files[@]}" | inigrep $ig_limit $ig_mode "$ig_query"
|
117
|
|
- return "${PIPESTATUS[0]}"
|
118
|
|
-}
|
119
|
|
-
|
120
|
|
-saturnin__get() {
|
121
|
|
- #
|
122
|
|
- # Show Saturnin Internal info by key $1 and exit
|
123
|
|
- #
|
124
|
|
- # Key $1 can be whole `--saturnin-get-stuff` argument or just
|
125
|
|
- # the part after `--saturnin-get-`.
|
126
|
|
- #
|
127
|
|
- # This is aimed to help debugging and testing the app (or
|
128
|
|
- # Saturnin itself) by showing packaging and deployment related
|
129
|
|
- # info.
|
130
|
|
- #
|
131
|
|
- local key=${1#--saturnin-get-}
|
132
|
|
- case "$key" in
|
133
|
|
- shellfu-path) echo "$SHELLFU_PATH" ;;
|
134
|
|
- saturnin-conf-path) echo "$SATURNIN_CONF_PATH" ;;
|
135
|
|
- app-git-hash) echo "$SATURNIN_APP_GIT_HASH" ;;
|
136
|
|
- app-version) echo "$SATURNIN_APP_VERSION" ;;
|
137
|
|
- cache-home) echo "$SATURNIN_CACHE_HOME" ;;
|
138
|
|
- libexec) echo "$SATURNIN_LIBEXEC" ;;
|
139
|
|
- libexec-prefix) echo "$SATURNIN_LIBEXEC_PREFIX" ;;
|
140
|
|
- *) warn "unknown devel key: $key"
|
141
|
|
- exit "$EXIT_USAGE" ;;
|
142
|
|
- esac
|
143
|
|
- exit "$EXIT_OK"
|
144
|
|
-}
|
145
|
|
-
|
146
|
|
-saturnin__lssc() {
|
147
|
|
- #
|
148
|
|
- # List subcommands
|
149
|
|
- #
|
150
|
|
- echo conf
|
151
|
|
- find "$SATURNIN_LIBEXEC" \
|
152
|
|
- -mindepth 1 \
|
153
|
|
- -maxdepth 1 \
|
154
|
|
- -executable \
|
155
|
|
- | sed -e "s|^.*/||; s|^$SATURNIN_LIBEXEC_PREFIX||" \
|
156
|
|
- | sort
|
157
|
|
-}
|
158
|
|
-
|
159
|
|
-saturnin__main() {
|
160
|
|
- local subcommand
|
161
|
|
- while true; do case $1 in
|
162
|
|
- -d|--debug) export PRETTY_DEBUG=true; shift ;;
|
163
|
|
- -v|--verbose) export PRETTY_VERBOSE=true; shift ;;
|
164
|
|
- -h|--help) saturnin__usage -e 0; exit ;;
|
165
|
|
- --version) saturnin__version; exit ;;
|
166
|
|
- --version-semver) saturnin__get app-version ;;
|
167
|
|
- --saturnin-get-*) saturnin__get "$1" ;;
|
168
|
|
- -*) saturnin__usage; ;;
|
169
|
|
- --*) saturnin__usage; ;;
|
170
|
|
- --) shift; break ;;
|
171
|
|
- "") saturnin__usage; ;;
|
172
|
|
- *) break; ;;
|
173
|
|
- esac done
|
174
|
|
- subcommand="$1"; shift
|
175
|
|
- debug -v SHELLFU_PATH SATURNIN_LIBEXEC SATURNIN_CONF_PATH
|
176
|
|
- case "$subcommand" in
|
177
|
|
- conf) saturnin__conf "$@" ;;
|
178
|
|
- _ls_sc) saturnin__lssc ;;
|
179
|
|
- _lsfun) shellfu-get lsfun ;;
|
180
|
|
- _lsmod) shellfu-get lsmod ;;
|
181
|
|
- *) saturnin__runsc "$subcommand" "$@" ;;
|
182
|
|
- esac
|
183
|
|
-}
|
184
|
|
-
|
185
|
|
-saturnin__conf_mkpath() {
|
186
|
|
- #
|
187
|
|
- # Assemble SATURNIN_CONF_PATH from locations $@
|
188
|
|
- #
|
189
|
|
- # For each location, print colon-delimited list of
|
190
|
|
- # directories. If location ends with "/ini.d", list of
|
191
|
|
- # subfolders, sorted by C locale is printed--this allows
|
192
|
|
- # for modular configuration. Otherwise the location
|
193
|
|
- # is printed. Non-existent or non-directory locations
|
194
|
|
- # are silently ignored.
|
195
|
|
- #
|
196
|
|
- local location # one location argument
|
197
|
|
- local path # one path listed
|
198
|
|
- for location in "$@";
|
199
|
|
- do
|
200
|
|
- test -d "$location" || continue
|
201
|
|
- case "$location" in
|
202
|
|
- */ini.d) # modular location--sort subfolders
|
203
|
|
- find -L "$location" -mindepth 1 -maxdepth 1 -type d \
|
204
|
|
- | LC_ALL=C sort
|
205
|
|
- ;;
|
206
|
|
- *)
|
207
|
|
- echo "$location"
|
208
|
|
- ;;
|
209
|
|
- esac
|
210
|
|
- done \
|
211
|
|
- | _saturnin__nl2colon
|
212
|
|
-}
|
213
|
|
-
|
214
|
|
-saturnin__runhook() {
|
215
|
|
- #
|
216
|
|
- # Run custom hook
|
217
|
|
- #
|
218
|
|
- local hname="$1"
|
219
|
|
- local hook_code
|
220
|
|
- test -n "$SATURNIN_SUBCOMMAND" || {
|
221
|
|
- warn "unknown subcommand, ignoring hook: $hname"
|
222
|
|
- return 0
|
223
|
|
- }
|
224
|
|
- hook_code="$(saturnin__conf -j "hook.$SATURNIN_SUBCOMMAND.$hname")"
|
225
|
|
- debug -v SATURNIN_SUBCOMMAND hook_code hname
|
226
|
|
- bash -n <<<"$hook_code" || {
|
227
|
|
- warn "syntax errors, ignoring hook: $hname"
|
228
|
|
- return 0
|
229
|
|
- }
|
230
|
|
- eval "$hook_code"
|
231
|
|
-}
|
232
|
|
-
|
233
|
|
-saturnin__runsc() {
|
234
|
|
- #
|
235
|
|
- # Run subcommand $SATURNIN_SUBCOMMAND
|
236
|
|
- #
|
237
|
|
- local subcommand="$1"; shift
|
238
|
|
- local binpath # path to subcommand's binary
|
239
|
|
- binpath+="$SATURNIN_LIBEXEC/"
|
240
|
|
- binpath+="$SATURNIN_LIBEXEC_PREFIX$subcommand"
|
241
|
|
- debug -v binpath
|
242
|
|
- debug "\$*='$*'"
|
243
|
|
- test -x "$binpath" || {
|
244
|
|
- warn "invalid sub-command: $subcommand"
|
245
|
|
- saturnin__usage
|
246
|
|
- }
|
247
|
|
- SATURNIN_SUBCOMMAND="$subcommand" "$binpath" "$@"
|
248
|
|
-}
|
249
|
|
-
|
250
|
|
-saturnin__usage() {
|
251
|
|
- #
|
252
|
|
- # Show usage message and exit
|
253
|
|
- #
|
254
|
|
- #shellcheck disable=SC2046
|
255
|
|
- mkusage "$@" \
|
256
|
|
- "[options] COMMAND [ARG...]" \
|
257
|
|
- -o \
|
258
|
|
- "-d, --debug turn on debugging" \
|
259
|
|
- "-h, --help show this help message and exit"\
|
260
|
|
- "-v, --verbose turn on verbosity" \
|
261
|
|
- "--version show version and exit" \
|
262
|
|
- -c \
|
263
|
|
- $(saturnin__lssc)
|
264
|
|
-}
|
265
|
|
-
|
266
|
|
-saturnin__version() {
|
267
|
|
- #
|
268
|
|
- # Print version info
|
269
|
|
- #
|
270
|
|
- local tagline=${SATURNIN_APP_TAGLINE:-Some app with default tagline}
|
271
|
|
- local maybe_codename=""
|
272
|
|
- test -n "$SATURNIN_APP_CODENAME" && maybe_codename=" - $SATURNIN_APP_CODENAME"
|
273
|
|
- echo "$(basename "$0") ($tagline) $SATURNIN_APP_VERSION$maybe_codename"
|
274
|
|
- return "$EXIT_OK"
|
275
|
|
-}
|
276
|
|
-
|
277
|
|
-saturnin__wraphook() {
|
278
|
|
- #
|
279
|
|
- # Wrap command "$@" in hooks
|
280
|
|
- #
|
281
|
|
- # Run pre hook, then "$@", then post hook. Always exit
|
282
|
|
- # with status of "$@", even if hooks fail. Ignore
|
283
|
|
- # post-hook if "$@" failed.
|
284
|
|
- #
|
285
|
|
- local es=0
|
286
|
|
- saturnin__runhook pre
|
287
|
|
- "$@" || return $?
|
288
|
|
- es=$?
|
289
|
|
- saturnin__runhook post
|
290
|
|
- return $es
|
291
|
|
-}
|
292
|
|
-
|
293
|
|
-
|
294
|
|
-# # that what you see below this line #
|
295
|
|
-# INTERNAL # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
|
296
|
|
-# # use in your code to anger the divine #
|
297
|
|
-
|
298
|
|
-_saturnin__conf__merge() {
|
299
|
|
- #
|
300
|
|
- # Take paths and applying merge strategy, load file(s)
|
301
|
|
- #
|
302
|
|
- local path
|
303
|
|
- local found=false
|
304
|
|
- while read -r path;
|
305
|
|
- do
|
306
|
|
- test -f "$path" || continue
|
307
|
|
- found=true
|
308
|
|
- case $Strategy in
|
309
|
|
- first)
|
310
|
|
- debug "winner: $path"
|
311
|
|
- cat "$path"
|
312
|
|
- cat >/dev/null # throw away rest of paths
|
313
|
|
- ;;
|
314
|
|
- join)
|
315
|
|
- echo "# file: ${path/$HOME/~}"
|
316
|
|
- cat "$path" 2>/dev/null
|
317
|
|
- ;;
|
318
|
|
- esac
|
319
|
|
- done
|
320
|
|
- $found
|
321
|
|
-}
|
322
|
|
-
|
323
|
|
-_saturnin__conf__load() {
|
324
|
|
- #
|
325
|
|
- # Print contents of files specified in $@
|
326
|
|
- #
|
327
|
|
- # Each argument means possible file candidate. If candidate
|
328
|
|
- # contains slash, it's treated as file path and is printed
|
329
|
|
- # directly. If it's single dash, standard input is copied.
|
330
|
|
- #
|
331
|
|
- # In all other cases, filename is searched in all elements
|
332
|
|
- # of SATURNIN_CONF_PATH; output then depends on chosen $Strategy:
|
333
|
|
- # with 'first' strategy, first existing file is printed, with
|
334
|
|
- # 'join' strategy. all existing files are printed.
|
335
|
|
- #
|
336
|
|
- local arg trydir trypath es
|
337
|
|
- es=0
|
338
|
|
- for arg in "$@";
|
339
|
|
- do
|
340
|
|
- case $arg in
|
341
|
|
- -|*/*) # stdin, or path (with slash)
|
342
|
|
- cat "$arg" || es=3
|
343
|
|
- ;;
|
344
|
|
- *) # name given, find all its incarnations
|
345
|
|
- debug -v SATURNIN_CONF_PATH
|
346
|
|
- echos "$SATURNIN_CONF_PATH" \
|
347
|
|
- | tr ':' '\n' \
|
348
|
|
- | while read -r trydir;
|
349
|
|
- do
|
350
|
|
- test -n "$trydir" || continue
|
351
|
|
- trypath="$trydir/$arg"
|
352
|
|
- echos "$trypath"
|
353
|
|
- done \
|
354
|
|
- | _saturnin__conf__merge; es=$?
|
355
|
|
- ;;
|
356
|
|
- esac
|
357
|
|
- done
|
358
|
|
- return $es
|
359
|
|
-}
|
360
|
|
-
|
361
|
|
-_saturnin__conf_usage() {
|
362
|
|
- #
|
363
|
|
- # Show usage message and exit
|
364
|
|
- #
|
365
|
|
- PRETTY_USAGE="self=${0##*/} conf" \
|
366
|
|
- mkusage "$@" \
|
367
|
|
- "[options] [-e] SECTION.KEY [FNAME]" \
|
368
|
|
- "[options] -r SECTION.KEY [FNAME]" \
|
369
|
|
- "[options] -K SECTION [FNAME]" \
|
370
|
|
- "[options] -P FNAME" \
|
371
|
|
- "[options] -S FNAME" \
|
372
|
|
- -- \
|
373
|
|
- "Use inigrep to query config files." \
|
374
|
|
- -o \
|
375
|
|
- "-j join all files before applying query" \
|
376
|
|
- "-1 ensure single line is returned" \
|
377
|
|
- -c \
|
378
|
|
- "-e use normal mode (default)" \
|
379
|
|
- "-r use raw mode (preserves RHS whitespace and some comments)" \
|
380
|
|
- "-K list available keys in SECTION" \
|
381
|
|
- "-S list available sections in FNAME" \
|
382
|
|
- "-P list available keypaths (SECTION.KEY) in FNAME" \
|
383
|
|
- -- \
|
384
|
|
- "FNAME is filename, which is then searched on all paths specified" \
|
385
|
|
- "in SATURNIN_CONF_PATH and depending on -j parameter, first one" \
|
386
|
|
- "wins or all are joined. If FNAME contains slash, this search is" \
|
387
|
|
- "not done and FNAME is taken as path to file that is then queried."\
|
388
|
|
- "" \
|
389
|
|
- "If FNAME is omitted, it is inferred from SECTION (e.g. .'foo.ini'"\
|
390
|
|
- "if 'foo.bar' was section name; note that section name may contain"\
|
391
|
|
- "dot)."
|
392
|
|
-}
|
393
|
|
-
|
394
|
|
-_saturnin__nl2colon() {
|
395
|
|
- #
|
396
|
|
- # Convert newline-based list of paths to colon:based:list
|
397
|
|
- #
|
398
|
|
- # Empty paths must not be included in the resulting list,
|
399
|
|
- # so we need to drop them and also get the colons right.
|
400
|
|
- #
|
401
|
|
- local idx=0 # current item index (zero-based)
|
402
|
|
- local path
|
403
|
|
- while read -r path;
|
404
|
|
- do
|
405
|
|
- test -z "$path" && continue
|
406
|
|
- test $idx -gt 0 && echo -n ':'
|
407
|
|
- echo -n "$path"
|
408
|
|
- ((idx++))
|
409
|
|
- done
|
410
|
|
-}
|