saturnin.sh.skel 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. #!/bin/bash
  2. shellfu import exit
  3. shellfu import inigrep
  4. shellfu import pretty
  5. #
  6. # Saturnin - __MKIT_PROJ_TAGLINE__
  7. #
  8. # Saturnin is a Shellfu module that can help you build consistent and easy
  9. # to use command line-based toolkit. The idea is that you have a set of
  10. # scripts that together form a particular tool, which you then integrate
  11. # under a single meta-command (similar to git, apt, systemctl and many
  12. # others...).
  13. #
  14. # Saturnin's main goal is to help you with the integration so that you can
  15. # fully concentrate on development of the scripts, while Saturnin will
  16. # provide the common part of command-line interface. Adding to that,
  17. # Saturnin also provides several extra features as config management,
  18. # hooks or support for command line completion.
  19. #
  20. # It's possible to integrate any kind of programs within Saturnin, although
  21. # extra features are currently only available for Shellfu/Bash scripts.
  22. #
  23. # Starting your toolkit
  24. # =====================
  25. #
  26. # The starting point of a Saturnin-powered toolkit is the main meta-command,
  27. # which sets all mandatory variables and calls saturnin__main(). You could
  28. # implement that one yourself, but it's **strongly recommended** to start
  29. # off by creating a copy of 'app.skel' from 'saturnin-demo':
  30. #
  31. # https://github.com/AloisMahdal/saturnin-demo/
  32. #
  33. # In fact, it's best to actually copy the *whole* project, which serves
  34. # as skeleton of not just the meta-command (by Saturnin) but also MKit
  35. # install/build system, which provides features like installation
  36. # scripts, SemVer-compatible versioning and packaging templates for both
  37. # RPM and DEB:
  38. #
  39. # git clone https://github.com/AloisMahdal/saturnin-demo
  40. # mv saturnin-demo myproject
  41. # cd myproject
  42. # rm -rf .git
  43. # git init
  44. #
  45. # You will need to edit few files, for starters:
  46. #
  47. # edit mkit.ini # your main project info
  48. # edit packaging/template.spec # file lists, Requires and description
  49. # edit packaging/debian/control # ..also for Debian
  50. # git add .
  51. # git commit -m "Initial commit"
  52. #
  53. # ...but the reward is sweet:
  54. #
  55. # make install # to install to current system
  56. #
  57. # make rpmstuff # to get .spec file and source tarball
  58. # make debstuff # to get debian directory
  59. # make vbump # to seal off release
  60. # make release # to seal off release
  61. #
  62. #
  63. # Git commit hash of application source tree
  64. #
  65. # This is supposed to be set by your build scripts when building your
  66. # application. The string is returned by calling your meta-command with
  67. # option --saturnin-app-git-hash.
  68. #
  69. # Look for 'satcmd' template for a working example.
  70. #
  71. SATURNIN_APP_GIT_HASH=${SATURNIN_APP_GIT_HASH:-}
  72. #
  73. # Your application version
  74. #
  75. # This is supposed to be set by your build scripts when building your
  76. # application. The string is returned by calling your meta-command with
  77. # options --version, --version-semver or --saturnin-app-version.
  78. #
  79. # Look for 'satcmd' template for a working example.
  80. #
  81. SATURNIN_APP_VERSION=${SATURNIN_APP_VERSION:-}
  82. #
  83. # Path to user cache
  84. #
  85. SATURNIN_CACHE_HOME=${SATURNIN_CACHE_HOME:-}
  86. #
  87. # Path where saturnin__conf should look for files
  88. #
  89. # If filename does not contain slash, it is looked up in each (or all,
  90. # based on strategy--see saturnin__conf() doc) path in this list. The
  91. # list is colon-separated and non-dirs as well as empty strings are
  92. # silently ignored.
  93. #
  94. SATURNIN_CONF_PATH="${SATURNIN_CONF_PATH:-}"
  95. #
  96. # Expected config filename extension (for guessing from path head)
  97. #
  98. # If no filename to read is given, saturnin__conf() will guess filename
  99. # as the path head plus this suffix (e.g. `foo.ini` for `saturnin__conf
  100. # foo.bar.baz`)
  101. #
  102. SATURNIN_CONF_SUFFIX="${SATURNIN_CONF_SUFFIX:-.ini}"
  103. #
  104. # Directory where to look for subcommands
  105. #
  106. # Files here starting with $SATURNIN_LIBEXEC_PREFIX are considered
  107. # subcommands
  108. #
  109. SATURNIN_LIBEXEC="${SATURNIN_LIBEXEC:-}"
  110. #
  111. # Subcommand file prefix
  112. #
  113. # This is recommended to be set to meta-command name plus dash. For
  114. # example, if your meta-command is `mykit`, this should be set to
  115. # `mykit-`.
  116. #
  117. SATURNIN_LIBEXEC_PREFIX="${SATURNIN_LIBEXEC_PREFIX:-}"
  118. #
  119. # Meta-command help mode
  120. #
  121. # This controls what is displayed when user calls meta-command with --help
  122. # argument. Following formats are supported:
  123. #
  124. # +HELPFILE
  125. # =HELPFILE
  126. #
  127. # In both cases, HELPFILE must be absolute path to a file containing
  128. # human-readable description of the meta-command, which will be directly
  129. # presented to user.
  130. #
  131. # If the leading character is '=' (equal sign), the help text consists of,
  132. # and only of the HELPFILE. If the character is '+' (plus sign), the help
  133. # text is pre-pended with auto-generated usage message.
  134. #
  135. # If the value is empty, only the auto-generated usage message is printed.
  136. #
  137. SATURNIN_META_HELP="${SATURNIN_META_HELP:-}"
  138. #
  139. # Current running subcommand
  140. #
  141. # The value is set and exported into sub-command binary at moment of
  142. # saturnin__main() call.
  143. #
  144. SATURNIN_SUBCOMMAND="${SATURNIN_SUBCOMMAND:-}"
  145. saturnin__bug() {
  146. #
  147. # Warn about bug in your software
  148. #
  149. # Issue warning using warn() from pretty but also add application
  150. # version. This is useful when an assertion in your application fails
  151. # such that it is certain that there is a bug inside it.
  152. #
  153. # In such cases, it makes sense to print also version information to
  154. # help users with reporting.
  155. #
  156. local msg # message core
  157. for msg in "$@";
  158. do
  159. warn "bug: $msg"
  160. done
  161. warn "bug in $(basename "$0") version: $SATURNIN_APP_VERSION"
  162. }
  163. saturnin__conf() {
  164. #
  165. # inigrep smart loader
  166. #
  167. # Usage:
  168. # saturnin__conf [-j] [inigrep-query] [-- [file]..]
  169. #
  170. #
  171. # File arguments
  172. # ==============
  173. #
  174. # If omitted, *file* argument is inferred by taking part of kpath name
  175. # before first dot and appending value of `$SATURNIN_CONF_SUFFIX`,
  176. # (".ini" by default).
  177. #
  178. # Each *file* argument is then processed as follows:
  179. #
  180. # * `-` (single dash) is interpreted as reading from standard input.
  181. #
  182. # * If argument contains slash, it is expanded as a regular path
  183. # (relative or absolute).
  184. #
  185. # * Otherwise, it is taken as filename and searched for in directories
  186. # given in `$SATURNIN_CONF_PATH`. (This can yield more than one
  187. # path, which is equivalent as if all paths were provided.)
  188. #
  189. # Not all files expanded based on `$SATURNIN_CONF_PATH` are read by
  190. # default; reading is governed by "merge strategy": the default
  191. # strategy "first" reads only the first existing file.
  192. #
  193. # "join" strategy on the other hand, means that any files are simply
  194. # concatenated and prefixed with comment (visible only in raw mode)
  195. # containing path to the file.
  196. #
  197. # This means that if a section is queried that is present in both
  198. # files, it is effectively concatenated as well.
  199. #
  200. # Following calls are equivalent
  201. #
  202. # saturnin__conf foo.bar.baz
  203. # saturnin__conf foo.bar.baz foo.ini
  204. #
  205. # and both result in reading of key *baz* from section *foo.bar* in file
  206. # *foo.ini*, which is selected from *SATURNIN_CONF_PATH*. Should there
  207. # be more foo.ini's, the first is selected. Using `-j` switch
  208. #
  209. # saturnin__conf -j foo.bar.baz
  210. #
  211. # would cause all foo.ini's on *SATURNIN_CONF_PATH* be concatenated
  212. # instead.
  213. #
  214. local ig_mode # retrieval mode
  215. local ig_query # keypath or section name (when listing keys)
  216. local ig_limit # line limit
  217. local files=() # file specification
  218. local Strategy=first # merge strategy
  219. while true; do case $1:$2 in
  220. "":*) break ;;
  221. -j:*) Strategy=join; shift 1 ;;
  222. -1:*) ig_limit=$1; shift 1 ;;
  223. -e:*.*) ig_mode=$1; ig_query=$2; shift 2; break ;;
  224. -r:*.*) ig_mode=$1; ig_query=$2; shift 2; break ;;
  225. -K:*) ig_mode=$1; ig_query=$2; shift 2; break ;;
  226. -S:*) ig_mode=$1; ig_query=""; shift 1; break ;;
  227. -P:*) ig_mode=$1; ig_query=""; shift 1; break ;;
  228. .*:*) _saturnin__conf_usage -w "bad syntax: $*" ;;
  229. *.*:*) ig_mode=-e; ig_query=$1; shift 1; break ;;
  230. --help:*) _saturnin__conf_usage -e 0 ;;
  231. *) _saturnin__conf_usage -w "bad syntax: $*" ;;
  232. esac done
  233. test -n "$ig_mode" || _saturnin__conf_usage -w "could not determine inigrep mode"
  234. debug -v ig_limit ig_query ig_mode Strategy
  235. if test -n "$*";
  236. then
  237. files=("$@")
  238. elif test -n "$ig_query";
  239. then
  240. files=("${ig_query%%.*}$SATURNIN_CONF_SUFFIX")
  241. else
  242. _saturnin__conf_usage -w "dunno what to load"
  243. fi
  244. debug -v files
  245. #shellcheck disable=SC2086
  246. _saturnin__conf__load "${files[@]}" | inigrep $ig_limit $ig_mode "$ig_query"
  247. return "${PIPESTATUS[0]}"
  248. }
  249. saturnin__conf_find() {
  250. #
  251. # Find all existing instances of sub-path $1 on $SATURNIN_CONF_PATH
  252. #
  253. # Usage:
  254. #
  255. # saturnin__conf_find SUBPATH
  256. #
  257. # Go through all elements of $SATURNIN_CONF_PATH, looking for file or
  258. # directory, whose path is formed by joining SUBPATH to element of
  259. # $SATURNIN_CONF_PATH. Print each existing path, ignore rest.
  260. #
  261. # For example, with following setup:
  262. #
  263. # SATURNIN_CONF_PATH=foo:bar:baz
  264. # mkdir -p foo/one bar/one
  265. # mkdir -p bar/two/slashes
  266. #
  267. # call
  268. #
  269. # saturnin__conf_find one
  270. #
  271. # would print `foo/one` and `bar/one`, while
  272. #
  273. # saturnin__conf_find two/slashes
  274. #
  275. # would print `bar/two/slashes`.
  276. #
  277. # If at least one path was found, return zero. Otherwise, return one,
  278. # or more in case of error.
  279. #
  280. local file=$1 # sub-path to find
  281. local trydir # each item of $SATURNIN_CONF_PATH
  282. local trypath # each combined path
  283. debug -v SATURNIN_CONF_PATH
  284. echos "$SATURNIN_CONF_PATH" \
  285. | tr ':' '\n' \
  286. | while read -r trydir;
  287. do
  288. test -n "$trydir" || continue
  289. trypath="$trydir/$file"
  290. test -e "$trypath" || continue
  291. echos "$trypath"
  292. done \
  293. | grep .
  294. }
  295. saturnin__get() {
  296. #
  297. # Show Saturnin internal info by key $1 and exit
  298. #
  299. # Key $1 can be whole `--saturnin-get-stuff` argument or just the part
  300. # after `--saturnin-get-`.
  301. #
  302. # This is aimed to help debugging and testing the app (or Saturnin
  303. # itself) by showing packaging and deployment related info.
  304. #
  305. local key=${1#--saturnin-get-} # internal info key
  306. case "$key" in
  307. saturnin-conf-path) echo "$SATURNIN_CONF_PATH" ;;
  308. saturnin-version) echo "__MKIT_PROJ_VERSION__" ;;
  309. app-git-hash) echo "$SATURNIN_APP_GIT_HASH" ;;
  310. app-version) echo "$SATURNIN_APP_VERSION" ;;
  311. cache-home) echo "$SATURNIN_CACHE_HOME" ;;
  312. libexec) echo "$SATURNIN_LIBEXEC" ;;
  313. libexec-prefix) echo "$SATURNIN_LIBEXEC_PREFIX" ;;
  314. subcommands) saturnin__lssc ;;
  315. *) warn "unknown devel key: $key"
  316. exit "$EXIT_USAGE" ;;
  317. esac
  318. exit "$EXIT_OK"
  319. }
  320. saturnin__lssc() {
  321. #
  322. # List subcommands
  323. #
  324. find "$SATURNIN_LIBEXEC" \
  325. -mindepth 1 \
  326. -maxdepth 1 \
  327. -executable \
  328. -name "$SATURNIN_LIBEXEC_PREFIX*" \
  329. | sed -e "s|^.*/||; s|^$SATURNIN_LIBEXEC_PREFIX||" \
  330. | sort
  331. }
  332. saturnin__main() {
  333. #
  334. # Main meta-command entry function
  335. #
  336. # After setting all mandatory environment variables, call this from your
  337. # main meta-command script.
  338. #
  339. local subcommand # subcommand to execute (first non-option)
  340. test -n "$SATURNIN_CACHE_HOME" || die "SATURNIN_CACHE_HOME is not set"
  341. test -n "$SATURNIN_LIBEXEC" || die "SATURNIN_LIBEXEC is not set"
  342. test -n "$SATURNIN_LIBEXEC_PREFIX" || die "SATURNIN_LIBEXEC_PREFIX is not set"
  343. while true; do case $1 in
  344. -D|--full-debug) export PRETTY_DEBUG=true
  345. export PRETTY_DEBUG_EXCLUDE=""
  346. shift ;;
  347. -d|--debug) export PRETTY_DEBUG=true; shift ;;
  348. -v|--verbose) export PRETTY_VERBOSE=true; shift ;;
  349. -h|--help) saturnin__help; exit ;;
  350. --version) saturnin__version; exit ;;
  351. -V|--version-semver) saturnin__get app-version ;;
  352. --saturnin-get-*) saturnin__get "$1" ;;
  353. -*) saturnin__usage -w "unknown argument: $1" ;;
  354. --*) saturnin__usage -w "unknown argument: $1" ;;
  355. --) shift; break ;;
  356. "") saturnin__usage -w "too few arguments" ;;
  357. *) break; ;;
  358. esac done
  359. subcommand="$1"; shift
  360. PRETTY_DEBUG_EXCLUDE="" debug -v SATURNIN_APP_VERSION BASH_VERSION
  361. debug -v SATURNIN_CONF_PATH
  362. case "$subcommand" in
  363. conf) saturnin__conf "$@" ;;
  364. *) saturnin__runsc "$subcommand" "$@" ;;
  365. esac
  366. }
  367. saturnin__help() {
  368. #
  369. # Print meta-command help text
  370. #
  371. # See $SATURNIN_META_HELP for details.
  372. #
  373. local introline # introduction line
  374. introline=$(basename "$0")
  375. test -n "$SATURNIN_APP_TAGLINE" \
  376. && introline+=" - $SATURNIN_APP_TAGLINE"
  377. case "$SATURNIN_META_HELP" in
  378. "")
  379. echo "$introline"$'\n' >&2
  380. saturnin__usage -E -e 0
  381. ;;
  382. +/*)
  383. echo "$introline"$'\n' >&2
  384. saturnin__usage -E
  385. echo >&2
  386. _saturnin__cat_helpfile "${SATURNIN_META_HELP:1}"
  387. ;;
  388. =/*)
  389. _saturnin__cat_helpfile "${SATURNIN_META_HELP:1}"
  390. ;;
  391. *)
  392. echo "$introline"$'\n' >&2
  393. saturnin__usage -E
  394. saturnin__bug "malformed SATURNIN_META_HELP: $SATURNIN_META_HELP"
  395. return 3
  396. ;;
  397. esac
  398. }
  399. saturnin__conf_mkpath() {
  400. #
  401. # Compose new value for $SATURNIN_CONF_PATH from locations $@
  402. #
  403. # Usage:
  404. #
  405. # saturnin__conf_mkpath DIR [DIR..]
  406. #
  407. # Go through each DIR and print it, unless it ends with "/ini.d",
  408. # in which case list its subdirectories, sorted by C locale (this allows
  409. # for modular configuration).
  410. #
  411. # Non-existent or non-directory items are silently ignored.
  412. #
  413. local location # one location argument
  414. local path # one path listed
  415. for location in "$@";
  416. do
  417. test -d "$location" || continue
  418. case "$location" in
  419. */ini.d) # modular location--sort subfolders
  420. find -L "$location" -mindepth 1 -maxdepth 1 -type d \
  421. | LC_ALL=C sort
  422. ;;
  423. *)
  424. echo "$location"
  425. ;;
  426. esac
  427. done \
  428. | _saturnin__nl2colon
  429. }
  430. saturnin__runhook() {
  431. #
  432. # Run custom hook named $1 from respective configuration section
  433. #
  434. # Will load joined multi-line key "hook.$SATURNIN_SUBCOMMAND.$1" and
  435. # unless syntax check fails, execute it as Bash code (in separate
  436. # process).
  437. #
  438. local name="$1" # hook name
  439. local code # ... code
  440. test -n "$SATURNIN_SUBCOMMAND" || {
  441. warn "unknown subcommand, ignoring hook: $name"
  442. return 0
  443. }
  444. code="$(saturnin__conf -j "hook.$SATURNIN_SUBCOMMAND.$name")"
  445. debug -v SATURNIN_SUBCOMMAND code name
  446. bash -n <<<"$code" || {
  447. warn "syntax errors, ignoring hook: $name"
  448. return 0
  449. }
  450. bash <<<"$code"
  451. }
  452. saturnin__runsc() {
  453. #
  454. # Run subcommand $1 with arguments $2..
  455. #
  456. local subcommand="$1"; shift # subcommand to run
  457. local binpath # path to subcommand's binary
  458. binpath+="$SATURNIN_LIBEXEC/"
  459. binpath+="$SATURNIN_LIBEXEC_PREFIX$subcommand"
  460. debug -v binpath
  461. debug "\$*='$*'"
  462. test -x "$binpath" \
  463. || saturnin__usage -w "invalid sub-command: $subcommand"
  464. SATURNIN_SUBCOMMAND="$subcommand" "$binpath" "$@"
  465. }
  466. saturnin__usage() {
  467. #
  468. # Show usage message and exit
  469. #
  470. #shellcheck disable=SC2046
  471. mkusage "$@" \
  472. "[options] COMMAND [ARG...]" \
  473. -o \
  474. "-D, --full-debug turn on gory debugging" \
  475. "-V, --version show version and exit" \
  476. "-d, --debug turn on debugging" \
  477. "-h, --help show this help message and exit"\
  478. "-v, --verbose turn on verbosity" \
  479. -c \
  480. $(saturnin__lssc)
  481. }
  482. saturnin__version() {
  483. #
  484. # Print human-readable version info
  485. #
  486. # Basic version info is already stored in $SATURNIN_APP_VERSION,
  487. # this function prints more descriptive paragraph including Saturnin's
  488. # own version.
  489. #
  490. echo -n "$(basename "$0")"
  491. test -n "$SATURNIN_APP_TAGLINE" \
  492. && echo -n " ($SATURNIN_APP_TAGLINE)"
  493. echo -n " $SATURNIN_APP_VERSION"
  494. test -n "$SATURNIN_APP_CODENAME" \
  495. && echo -n " - $SATURNIN_APP_CODENAME"
  496. echo
  497. echo -n "Powered by Saturnin (__MKIT_PROJ_TAGLINE__)"
  498. echo -n " __MKIT_PROJ_VERSION__"
  499. echo -n " - __MKIT_PROJ_CODENAME__"
  500. echo
  501. return "$EXIT_OK"
  502. }
  503. saturnin__wraphook() {
  504. #
  505. # Wrap command $@ in hooks 'pre' and 'post'
  506. #
  507. # Run pre hook, then command $@, then post hook. Always exit with
  508. # status of the payload command, even if hooks fail. Ignore post-hook
  509. # if payload command failed.
  510. #
  511. local es=0 # exit status of this function
  512. saturnin__runhook pre
  513. "$@" || return $?
  514. es=$?
  515. saturnin__runhook post
  516. return $es
  517. }
  518. # # that what you see below this line #
  519. # INTERNAL # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
  520. # # use in your code to anger the divine #
  521. _saturnin__cat_helpfile() {
  522. #
  523. # Print helpfile $1
  524. #
  525. local helpfile=$1 # path to help file
  526. cat "$helpfile" >&2 && return 0
  527. saturnin__bug "cannot print help file: $helpfile"
  528. return 3
  529. }
  530. _saturnin__conf__merge() {
  531. #
  532. # Take paths and applying merge strategy, load file(s)
  533. #
  534. local path # every path
  535. local found=false # 'true' if we got any path
  536. while read -r path;
  537. do
  538. found=true
  539. case $Strategy in
  540. first)
  541. debug "winner: $path"
  542. cat "$path"
  543. cat >/dev/null # throw away rest of paths
  544. ;;
  545. join)
  546. echo "# file: ${path/$HOME/~}"
  547. cat "$path" 2>/dev/null
  548. ;;
  549. esac
  550. done
  551. $found
  552. }
  553. _saturnin__conf__load() {
  554. #
  555. # Print contents of files specified in $@
  556. #
  557. # Each argument means possible file candidate. If candidate contains
  558. # slash, it's treated as file path and is printed directly. If it's
  559. # single dash, standard input is copied.
  560. #
  561. # In all other cases, filename is searched in all elements of variable
  562. # SATURNIN_CONF_PATH; output then depends on chosen $Strategy: with
  563. # 'first' strategy, first existing file is printed, with 'join'
  564. # strategy, all existing files are printed.
  565. #
  566. local arg # each passed argument
  567. local es=0 # exit status of this function
  568. for arg in "$@";
  569. do
  570. case $arg in
  571. -|*/*) # stdin, or path (with slash)
  572. cat "$arg" || es=3
  573. ;;
  574. *) # name given, find all its incarnations
  575. saturnin__conf_find "$arg" \
  576. | _saturnin__conf__merge; es=$?
  577. ;;
  578. esac
  579. done
  580. return $es
  581. }
  582. _saturnin__conf_usage() {
  583. #
  584. # Show usage message, passing $@ to mkusage() and exit
  585. #
  586. PRETTY_USAGE="self=${0##*/} conf" \
  587. mkusage "$@" \
  588. "[options] [-e] SECTION.KEY [FNAME]" \
  589. "[options] -r SECTION.KEY [FNAME]" \
  590. "[options] -K SECTION [FNAME]" \
  591. "[options] -P FNAME" \
  592. "[options] -S FNAME" \
  593. -- \
  594. "Use inigrep to query config files." \
  595. -o \
  596. "-j join all files before applying query" \
  597. "-1 ensure single line is returned" \
  598. -c \
  599. "-e use normal mode (default)" \
  600. "-r use raw mode (preserves RHS whitespace and some comments)"\
  601. "-K list available keys in SECTION" \
  602. "-S list available sections in FNAME" \
  603. "-P list available keypaths (SECTION.KEY) in FNAME" \
  604. -- \
  605. "FNAME is filename, which is then searched on all paths specified"\
  606. "in SATURNIN_CONF_PATH and depending on -j parameter, first one" \
  607. "wins or all are joined. If FNAME contains slash, this search is"\
  608. "not done and FNAME is taken as path to file that is then" \
  609. "queried." \
  610. "" \
  611. "If FNAME is omitted, it is inferred from SECTION (e.g. 'foo.ini'"\
  612. "if 'foo.bar' was section name; note that section name itself may"\
  613. "contain dot)."
  614. }
  615. _saturnin__nl2colon() {
  616. #
  617. # Convert newline-based list of paths to colon:based:list
  618. #
  619. # Empty paths must not be included in the resulting list, so we need to
  620. # drop them and also get the colons right.
  621. #
  622. local idx=0 # current item index (zero-based)
  623. local path # each path on stdin
  624. while read -r path;
  625. do
  626. test -z "$path" && continue
  627. test $idx -gt 0 && echo -n ':'
  628. echo -n "$path"
  629. ((idx++))
  630. done
  631. }
  632. #shellfu module-version=__MKIT_PROJ_VERSION__