#!/bin/bash

##########################################################
# Copyright (c) 2001-2025 Alexey Kuryakin daqgroup@mail.ru
##########################################################

##########################################################
# Utility to read INI file section or value by key name.
##########################################################

################################
# Current script identification.
################################
readonly startupdir="$(pwd -LP)";
readonly scriptfile="${BASH_SOURCE[0]}";
readonly scriptname="$(basename $scriptfile)";
readonly scriptbase="$(basename $scriptfile .sh)";
readonly scripthome="$(dirname  $scriptfile)";
readonly scriptFILE="$(realpath $scriptfile)";
readonly scriptHOME="$(dirname  $scriptFILE)";

declare -i opt_ign=0; # option set flag ignore case
declare -i opt_raw=0; # option set flag raw section
declare opt_rem="#;"; # option set chars for remark

function do_version(){
 echo "$scriptname version 1.0";
};

function do_print_help(){
 do_version;
cat<<EOF
$scriptname Copyright (c) 2025 Alexey Kuryakin daqgroup@mail.ru
Utility to read INI file section or parameter key=value.
Usage:
 $scriptname [-options] [inifile section [keyname]]
Options:
 --version  - print version
 -h,--help  - print help screen
 -i,--ign   - ignore case: section/keyname is not case sensitive
 -r,--rem c - set chars (c) for remark, default chars is -r '#;'
 --raw      - print raw section's text, don't remove space chars
Parameters:
 inifile    - (mandatory) INI file to read
 section    - (mandatory) section name to read
 keyname    - (optional)  key name to read key=value
Notes:
 1) By default section/keyname is case sensitive. Use option '-i' to ignore case.
 2) By default remark starts with '#;' will drop. Use option '-r' to set remarks.
 3) By default output lead and trail  chars drop. Use option --raw to print AsIs.
 4) Section's text must be single-part and continuous, like standard Windows INI.
    Use 'readcfg' instead for multi-part sections with [ConfigFileList] support.
Examples:
 $scriptname --help
 $scriptname --version
 $scriptname -i -r '#' /etc/samba/smb.conf "[global]" "log file"
 $scriptname /usr/share/applications/CrossMachine.desktop "[Desktop Action Edit]"
 $scriptname /usr/share/applications/CrossMachine.desktop "[Desktop Action Edit]" Exec
EOF
};

function fatal(){
 1>&2 echo -ne "\n$2\n\n";
 exit $1;
};

function cat_section(){
 local ini="$1"; local sec="$2";
 sec="${sec/\[/}"; sec="${sec/\]/}";
 if [[ -r $ini ]]; then
  if [[ $opt_ign -eq 0 ]]; then
   cat "$ini" | sed -n -e "/^\s*\[$sec\]\s*$/,/^\s*\[.*\]\s*$/p";
  else
   local s1="$(cat "$ini" | grep -i -n "^\s*\[$sec\]\s*$" | cut -d: -f1)";
   local -i n1=${s1:-0}; if [[ $n1 -le 0 ]]; then return 1; else let n1++; fi;
   local s2="$(cat "$ini" | tail -n +$n1 | grep -n "^\s*\[.*\]\s*$" | head -n1 | cut -d: -f1)";
   local -i n2=${s2:-0}; if [[ $n2 -gt 0 ]]; then let n2--; fi;
   if [[ $n2 -gt 0 ]]; then
    cat "$ini" | tail -n +$n1 | head -n $n2;
   else
    cat "$ini" | tail -n +$n1;
   fi;
  fi;
 else
  fatal 1 "Error: missed file $ini";
 fi;
};

function filter_section(){
 local -a opt=();
 opt+=( -e '/^\s*\[.*\]\s*/d'      ); # drop sections [*]
 if [[ $opt_raw -eq 0 ]]; then
  if [[ -n $opt_rem ]]; then
   opt+=( -e "s/\s*[$opt_rem].*//" ); # drop remark
  fi;
  opt+=( -e '/^\s*$/d'             ); # drop empty lines
  opt+=( -e 's/\s*=\s*/=/'         ); # fix equals (=)
  opt+=( -e 's/^\s\s*//'           ); # drop lead spaces
  opt+=( -e 's/\s\s*$//'           ); # drop tail spaces
 fi;
 sed "${opt[@]}";
};

function extract_value(){
 local opt_i="-i";
 if [[ $opt_ign -eq 0 ]]; then opt_i=""; fi;
 grep $opt_i "^\s*$1\s*=\s*" | head -n1;
};

function readini_value(){
 readini_section "$1" "$2" | extract_value "$3";
};

function readini_section(){
 cat_section "$@" | filter_section;
};

function readini_handler(){
 case $# in
  2) readini_section "$1" "$2"; ;;
  3) readini_value   "$1" "$2" "$3"; ;;
  *) fatal 1 "Error: invalid number of arguments ($#)"; ;;
 esac;
};

######
# MAIN
######

function main(){
 if [[ $# -eq 0 ]]; then
  fatal 1 "Error: no arguments specified. Use -h option to get help.";
 fi;
 while [[ -n $1 ]]; do
  case $1 in
   --version)        do_version; return 0; ;;
   -h|-help|--help)  do_print_help; return 0; ;;
   -i|-ign|--ign)    let opt_ign=1; ;;
   -raw|--raw)       let opt_raw=1; ;;
   -r|-rem|--rem)    opt_rem="$2"; shift; ;;
   -*)               fatal 1 "Error: bad option $1"; ;;
   *)                break; ;;
  esac;
  shift;
 done;
 readini_handler "$@";
};

main "$@";

##############
## END OF FILE
##############
