123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- #!/bin/bash
-
- ffoo import pretty
-
-
- #
- # Expected filename extension (for guessing from -p path head)
- #
- # If no filename to read is given, inigrep will guess filename
- # as the path head plus this suffix (e.g. `foo.ini` for
- # `inigrep -p foo.bar.baz`)
- #
- FFOO_CONFIG_SUFFIX="${FFOO_CONFIG_SUFFIX:-.ini}"
-
-
- __inigrep__cat() {
- #
- # cat without starting a process
- #
- while IFS= read line;
- do echos "$line"; done
- }
-
-
- __inigrep__fltcmt() {
- #
- # Just strip comments
- #
- grep -v -e "^[[:space:]]*[#;]" -e "^[[:space:]]*$"
- }
-
-
- __inigrep__fltkey() {
- #
- # Filter values from `key = value pairs`
- #
- # In listing mode, prints *key names* instead
- #
- while IFS= read line; # ' key = val '
- do
- line="${line##*([[:space:]])}" # line='key = val '
- key="${line%%*([[:space:]])=*}" # key='key'
- value="${line##$key*([[:space:]])=}" # value=' val '
- $strict || value="${value##*([[:space:]])}" # value='val'
- test "$key" = "$wntkey" || continue
- $list || echos "$value"
- $list && echos "$line"
- done
- }
-
-
- __inigrep__fltsct() {
- #
- # Filter out only the wanted section body
- #
- # In listing mode prints also section name
- #
- local sct_ok=false
- local line
- while IFS= read line;
- do
- case "$line" in
- \[$wntsct\]) sct_ok=true; # our section
- $list && echos $line; # inform lister
- continue ;;
- \[*\]) sct_ok=false; continue ;; # next section
- esac
- $sct_ok || continue
- $strict || line="${line##*([[:space:]])}"
- echos "$line"
- done
- }
-
-
- __inigrep__fltlst() {
- #
- # Filter only section/keys/path names from the stream
- #
- local key="__NOKEY__" line="" sct="__NOSCT__"
- while read line;
- do
- case "$line" in
- \[*\]) sct=${line#[}; sct=${sct%]}; key=__NOKEY__ ;;
- *=*) key="${line%%*([[:space:]])=*}" ;; # key='key'
- *) continue ;; # whatever
- esac
- case "$listobj:$sct:$key" in
- p*:__NOSCT__:*) echos "$sct.$key" ;;
- p*:*:__NOKEY__) continue ;;
- p*:*:*) echos "$sct.$key" ;;
- s*:__NOSCT__:*) echos "$sct" ;;
- s*:*:__NOKEY__) echos "$sct" ;;
- s*:*:*) continue ;;
- k*:__NOSCT__:__NOKEY__) continue ;;
- k*:__NOSCT__:*) echos "$key" ;;
- k*:*:__NOKEY__) continue ;;
- k*:*:*) echos "$key" ;;
- esac
- done
- }
-
-
- __inigrep__merge() {
- #
- # Take paths and applying merge strategy, load file(s)
- #
- local path
- debug -v strategy
- while read path;
- do
- test -f "$path" || continue
- debug -v path
- case $strategy in
- first)
- debug "winner: $path"
- cat "$path"
- cat >/dev/null # throw away rest of paths
- ;;
- join)
- echo "# file: ${path/$HOME/~}"
- cat "$path" 2>/dev/null
- ;;
- esac
- done
- }
-
-
- __inigrep__load() {
- #
- # Find file(s) candidates and load them (or use *__merge)
- #
- test -n "$1" || __inigrep__load "${wntsct%%.*}$FFOO_CONFIG_SUFFIX"
- 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_CONFIG_PATH
- echos "$FFOO_CONFIG_PATH" \
- | tr ':' '\n' \
- | while read trydir;
- do
- test -n "$trydir" || continue
- trypath="$trydir/$arg"
- debug -v trypath
- echos "$trypath"
- done \
- | __inigrep__merge
- ;;
- esac
- done
- true
- }
-
-
- inigrep() {
- #
- # A flexible (not just) .ini file reader
- #
- # Usage:
- # inigrep [-S] [-1] [-j] -p section.key [file...]
- # inigrep [-S] [-1] [-j] [-s section] [-k key] [file...]
- #
- #
- # Selection
- # =========
- #
- # Probably most apparent use case is selection of key from
- # a section of INI file--the first usage form. Note that
- # *section* itself may contain dots; only last part after all
- # dots is parsed as *key*.
- #
- # The second form lets you 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, which allows for creating multi-line values.
- # Providing *-1* argument, however, always prints only one
- # line.
- #
- # Leading/trailing spaces and empty lines are removed from
- # output by default, but strict mode (*-S* or *--strict*
- # switch) changes this so 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
- # ==============
- #
- # If omitted, *file* argument is inferred by taking part of
- # section name before first dot and appending value of
- # `$FFOO_CONFIG_SUFFIX`, (".ini" by default).
- #
- # Each *file* argument is then processed as follows:
- #
- # * `-` (single dash) is interpreted as reading from
- # STDIN.
- #
- # * If argument contains slash, it is expanded as a regular
- # path (relative or absolute).
- #
- # * Otherwise, it is taken as filename and searched for
- # in directories given in `$FFOO_CONFIG_PATH`. (This can
- # yield more than one path, which is equivalent as if
- # all paths were provided.)
- #
- # Not all files expanded based on `$FFOO_CONFIG_PATH`
- # are read by default; reading is governed by "merge
- # strategy": the default strategy "first" reads only
- # the first existing file.
- #
- # "join" strategym on the other hand, means that any
- # files are simply concatenated and prefixed with
- # comment containing path to the file. (This means that
- # if a section is queried that is present in both files,
- # it is effectively concatenated as well.)
- #
- # Note that the comment can be seen only in strict mode
- # and will be included even for non-existent files.
- #
- # The merge strategy can be specified using parameter
- # `--merge-strategy`, while `-j` or `--join` is a
- # shorthand for `--merge-strategy join`
- #
- #
- # Examples
- # ========
- #
- # Following calls are equivalent
- #
- # inigrep -p foo.bar.baz
- # inigrep -p foo.bar.baz foo.ini
- #
- # and result in reading of key *baz* from section *foo.bar*
- # in file *foo.ini*, which is selected from *FFOO_CONFIG_PATH*.
- # Should there be more foo.ini's, the first is selected.
- # Using `-j` switch
- #
- # inigrep -j -p foo.bar.baz
- #
- # would cause any of such files be concatenated.
- #
- # inigrep -s qux ./corge.ini
- #
- # will open corge.ini in current directory, and print every
- # range of lines delimited by line containing only `[qux]`
- # and another section delimiter or end of file. Comments and
- # empty lines will be stripped.
- #
- # cat graply | inigrep -S -k grault -
- #
- # will split every line in graply that starts with word
- # "grault" surrounded by any amount of whitespace and
- # followed by equals sign, printing part after the equals
- # sign. The -S` (strict mode) will preserve *right hand
- # side*, ie. will not strip any spaces after "=".
- #
- local wntsct="" # desired section name
- local wntkey="" # desired key name
- local list=false # listing mode?
- local listobj="paths" # what to list (paths/sections/keys)
- local strict=false # strict mode?
- local sct_ok=true # are we reading a desired section
- local one_line=false # limit to single line
- local grepex="." # i.e. throw away empty lines
- local strategy="first" # merge strategy
- 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) list=true; listobj=paths; shift 1 ;;
- -L|--list) list=true; listobj=$2; shift 2 ;;
- --merge-strategy) strategy="$2"; shift 2 ;;
- *) break ;;
- esac done
- case "$strategy" in
- first|join) : ;;
- *) warn "invalid merge strategy: $strategy"; return 2 ;;
- esac
- case $listobj in
- p|paths) : ;;
- k|keys) : ;;
- s|sections) : ;;
- *) warn "invalid object type to list: $listobj"; return 2 ;;
- esac
- fltsct=__inigrep__cat # section filter
- fltkey=__inigrep__cat # key filter
- fltcmt=__inigrep__fltcmt # comment filter
- fltlst=__inigrep__cat # listing mode filter
- limit_line=__inigrep__cat # limiter filter
- test -n "$wntsct" && fltsct=__inigrep__fltsct
- test -n "$wntkey" && fltkey=__inigrep__fltkey
- $list && fltlst=__inigrep__fltlst
- $one_line && limit_line="tail -1"
- $strict && grepex=""
- $strict && fltcmt=__inigrep__cat
- debug -v wntkey wntsct list listobj
- debug "\$@='$@'"
- local restore_extglob=$(shopt -p extglob)
- shopt -s extglob
- __inigrep__load "$@" \
- | $fltcmt \
- | $fltsct \
- | $fltkey \
- | $fltlst \
- | $limit_line \
- | grep "$grepex"
- $restore_extglob
- }
|