123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- #!/bin/bash
-
- ffoo import core
-
-
- #
- # Expected filename extension (for guessing from -p path head)
- #
- # If no filename to read is given, iniread will guess filename
- # as the path head plus this suffix (e.g. `foo.ini` for
- # `iniread -p foo.bar.baz`)
- #
- FFOO_INI_SUFFIX=".ini"
-
-
- __iniread__cat() {
- # cat without starting a process
- while IFS= read line;
- do echos "$line"; done
- }
-
-
- __initead_fltcmt() {
- grep -v -e "^[[:space:]]*[#;]" -e "^[[:space:]]*$"
- }
-
-
- __iniread__fltkey() {
- # filter values from key=value pair
- local line
- local key
- local value
- while IFS= read line;
- do
- line=$(sed -re 's/^\s*//' <<<"$line")
- key=$(sed -re 's/\s*=.*//; s/^\s*//' <<<"$line")
- if $strict;
- then
- value=$(sed -re 's/[^=]*=//' <<<"$line")
- else
- value=$(sed -re 's/[^=]*=\s*//' <<<"$line")
- fi
- if test "$key" = "$wntkey";
- then
- echos "$value"
- fi
- done
- }
-
-
- __iniread__fltsct() {
- # filter per section
- local sct_ok=false
- local line
- while IFS= read line;
- do
- if grep -qse "^\[$wntsct\]" <<<"$line";
- then
- sct_ok=true
- elif grep -qse "^\[" <<<"$line";
- then
- sct_ok=false
- elif $sct_ok;
- then
- $strict || line="$(sed -re 's/^\s*//' <<<"$line")"
- echos "$line"
- fi
- done
- }
-
-
- __iniread__merge() {
- local path
- debug -v strategy
- while read path;
- do
- debug -v path
- case $strategy in
- first)
- local winner
- if test -f "$path";
- then
- debug "winner: $path"
- cat "$path"
- cat >/dev/null # throw away rest of paths
- fi
- ;;
- join)
- debug -v path
- echo "# file: ${path/$HOME/~}"
- cat "$path" 2>/dev/null
- ;;
- esac
- done
- }
-
-
- __iniread__load() {
- if test -z "$1";
- then # guess filename from section head
- local guess="$(cut -d. -f1 <<<"$wntsct")$FFOO_INI_SUFFIX"
- debug -v guess
- __iniread__load "$guess"
- fi
- local arg trydir trypath
- for arg in "$@";
- do
- case $arg in
- -|*/*) # stdin, or path (with slash)
- debug -v arg
- cat "$arg"
- ;;
- *) # name given, find all its incarnations
- debug -v FFOO_INI_PATH
- echos "$FFOO_INI_PATH" \
- | tr ':' '\n' \
- | while read trydir;
- do
- test -n "$trydir" || continue
- trypath="$trydir/$arg"
- debug -v trypath
- echos "$trypath"
- done \
- | __iniread__merge
- ;;
- esac
- done
- true
- }
-
-
- iniread() {
- #
- # A flexible (not just) .ini file reader
- #
- # Usage:
- # iniread [-S] [-1] [-j] [-s section] [-k key] [file...]
- # iniread [-S] [-1] [-j] [-p path] [file...]
- #
- #
- # Selection
- # =========
- #
- # Probably most apparent use case is selection of key from
- # a section of INI file. This can be achieved using
- # *path*, which is equivalent to providing both *key* and
- # *section* (i.e. `-p section.key` is the same as
- # `-s section -k key`).
- #
- # However, you can also provide only *section* to select
- # only that section (leaving any key=value pairs intact),
- # or provide only *key* to select only that key from *all*
- # sections in file (if there are any). This in fact extends
- # use of this function beyond just INI-like format: if you
- # are brave enough, you could e.g. query a shell script for
- # a variable assignment (i.e. key=value pair).
- #
- # If suitable key is found at multiple lines, all values
- # are printed, unless you provided *-1* option. This allows
- # for creating multi-line values.
- #
- # Leading/trailing spaces and empty lines are removed from
- # output by default, but strict mode (*-S* or *--strict*
- # switch) changes this si that values after `=` are left
- # intact. Using strict mode when not selecting key or
- # section results in preserving empty lines and comments
- # as well.
- #
- #
- # File arguments
- # ==============
- #
- # Each *file* argument is processed as follows:
- #
- # * `-` (single dash), it is interprerted as reding from
- # STDIN.
- #
- # * If argument contains slash, it is expanded as a regular
- # path.
- #
- # * Otherwise, it is taken as filename and searched for
- # in directories given in `$FFOO_INI_PATH`. (This can
- # yield more than one path, which is equivalent as if
- # all paths were provided.)
- #
- # Not all files expanded based on `$FFOO_INI_PATH`
- # are read by default; reading is governed by "read
- # strategy": the default strategy "first" reads only
- # the first existing file. "join" strategy means
- # that any files are simply concatenated and prefixed
- # with comment containing path to the file, which can
- # be observed in strict mode. (This means that if a
- # section is queried that is present in both files,
- # it is effectively concatenated as well.
- #
- # The read strategy can be specified using parameter
- # `--read-strategy`, while `-j` or `--join` is a
- # shorthand for `--read-strategy join`
- #
- # Without *file* given at all, same procedure is used as in
- # case of filename without slash, except that *file* argument
- # is inferred by taking part of section name before first
- # dot and adding `$FFOO_INI_SUFFIX`, (".ini" by default).
- # This allows for creating relatively rich config value
- # structure it divided in several files (e.g. by component)
- # but accessing them with as little typing as possible.
- #
- local wntsct=""
- local wntkey=""
- local strict=false
- local sct_ok=true
- local one_line=false
- local grepex="." # i.e. throw away empty lines
- local strategy="first"
- while true; do case $1 in
- --) break ;;
- -1|--one-line) one_line=true; shift 1 ;;
- -j|--join) strategy="join"; shift 1 ;;
- -k|--key) wntkey="$2"; shift 2 ;;
- -p|--path) wntkey="${2##*.}"; wntsct="${2%.$wntkey}"; shift 2 ;;
- -s|--section) wntsct="$2"; sct_ok=false; shift 2 ;;
- -S|--strict) strict=true; shift 1 ;;
- -l|-L) warn "--list-* not implemented yet"; shift 1 ;;
- --read-strategy) strategy="$2"; shift 2 ;;
- *) break ;;
- esac done
- case "$strategy" in
- first|join) : ;;
- *) warn "invalid read strategy: $strategy"; return 2 ;;
- esac
- fltsct=__iniread__cat
- fltkey=__iniread__cat
- fltcmt=__initead_fltcmt
- limit_line=__iniread__cat
- test -n "$wntsct" && fltsct=__iniread__fltsct
- test -n "$wntkey" && fltkey=__iniread__fltkey
- $one_line && limit_line="tail -1"
- $strict && grepex=""
- $strict && fltcmt=__iniread__cat
- debug -v wntkey wntsct
- debug "\$@='$@'"
- __iniread__load "$@" \
- | $fltcmt \
- | $fltsct \
- | $fltkey \
- | $limit_line \
- | grep "$grepex"
- #TODO: list keys as section/key (with -s, list keys only)
- }
|