#!/bin/bash
# MKit - simple install helper
# See LICENSE file for copyright and license details.

__ini_cat() {
    #
    # A no-op for text stream
    #
    local line      # each line
    while read -r line; do
        printf -- "%s\n" "$line"
    done
}

__ini_expand() {
    #
    # Expand reference value (prefix only)
    #
    local line      # each input line
    local suffix    # tail of the line
    local ref       # reference
    local value     # value if reference
    while read -r line; do                  # [foo:bar]/path
        suffix="${line#\[*\]}"              # /path
        ref="${line%$suffix}"               # [foo:bar]
        ref="${ref%\]}"                     # [foo:bar
        ref="${ref#\[}"                     # foo:bar
        value="$(ini 1value "$ref")"        # foo_bar_value
        printf -- "%s\n" "$value$suffix"    # foo_bar_value/path
    done
}

__ini_grepcmt() {
    #
    # Remove comments from INI file on stdin
    #
    grep -v '^[[:space:]]*#'
}

__ini_grepkey() {
    #
    # Read key from a section
    #
    local wnt=$1    # wanted key
    grep '.' \
      | sed -e 's/ *= */=/; s/ +$//; s/^//;' \
      | grep -e "^$wnt=" \
      | cut -d= -f2- \
      | __ini_maybe_expand
}

__ini_greppath() {
    #
    # Read key from the right section
    #
    # E.g. `files:share:my/lib.sh` should read
    #
    #     [files:share]
    #         my/lib.sh   = proj/my/lib.sh
    #
    local wnt=$1                    # wanted path
    local wntkey=${wnt##*:}         # ^^ key part
    local wntsec=${wnt%:$wntkey}    # ^^ section part
    local override                  # ENV override (only ENV section)
    if test "$wntsec" = 'ENV'; then
        override=${!wntkey}
        test -n "$override" \
         && echo "$override" \
         && return
    fi
    __ini_grepsec "$wntsec" | __ini_grepkey "$wntkey"
}

__ini_grepsec() {
    #
    # Read one INI section
    #
    local wnt=$1        # wanted section name
    local ok=false      # are we in the section?
    local line          # each input line
    grep '.' \
      | while read -r line; do
            case "$line" in
                \[$wnt\]) ok=true;  continue ;;
                \[*\])    ok=false; continue ;;
            esac
            $ok || continue
            printf -- "%s\n" "$line"
        done \
      | sed -e 's/ *= */=/; s/ +$//; s/^//;'
}

__ini_lskeys() {
    #
    # List keys from a section
    #
    local sct=$1    # section of interest
    __ini_grepsec "$sct" | cut -d= -f1 | awk '!x[$0]++'
}

__ini_lssect() {
    #
    # List all section names
    #
    local arg=$1    # unused argument
    grep -x '\[.*\]' | sed 's/^.//; s/.$//'
}

__ini_maybe_expand() {
    #
    # Decide whether or not to expand
    #
    if test "$MKIT_INI_EXPAND" -gt 0; then
        MKIT_INI_EXPAND=$(( --MKIT_INI_EXPAND )) __ini_expand
    else
        __ini_cat
    fi
}

__ini_body() {
    #
    # Produce mkit.ini body including INCLUDE
    #
    # Note: recursive includes are not supported.
    #
    local inc                       # file to include
    local incre='\[INCLUDE:.*\]'    # include directive regex
    local iline                     # include directive line
    if iline=$(grep -m1 -x "$incre" "$MKIT_INI"); then
        inc=${iline#*:}; inc=${inc%]}
        grep -vx "$incre" "$inc"
        grep -vx "$incre" "$MKIT_INI"
    else
        cat "$MKIT_INI"
    fi | __ini_grepcmt
}

ini() {
    #
    # do ini operation
    #
    local op=$1             # operator
    local arg=$2            # argument
    local fn                # internal function implementing $op
    local limit=__ini_cat    # limiting internal function
    case $op in
        lskeys) fn=__ini_lskeys   ;;
        lssect) fn=__ini_lssect   ;;
        sec)    fn=__ini_grepsec  ;;
        values) fn=__ini_greppath ;;
        1value) fn=__ini_greppath; limit="tail -1" ;;
        *)      die "incorrect use of \`ini()\`"
    esac
    __ini_body | $fn "$arg" | $limit
}

update_version() {
    #
    # Change project:version in mkit.ini at path $2 to value $1
    #
    local version=$1    # new version
    local inifile=$2    # mkit.ini path
    local tmp           # mkit.ini cache
    tmp=$(mktemp -t mkit.update_version.XXXXXXXX)
    <"$inifile" perl -e '
        my $hit = 0;
        my $done = 0;
        foreach (<STDIN>) {
            if      ($done) { print; next; }
            elsif   (m/\[project\]/) { $hit++; print; next; }
            elsif   (m/\[/) { $hit = 0; print; next; }
            elsif   ($hit) { s/\bversion\b( *)=( *).*/version$1=$2$ARGV[0]/ and $done++; print; }
            else { print; next; }
        }
    ' "$version" >"$tmp" || die "failed to update version in mkit.ini"
    mv "$tmp" "$inifile"
}