#!/bin/sh
#        file: src/bootcdrst
#   copyright: Bernd Schumacher <bernd.schumacher@hpe.com> (2001-2019)
#     license: GNU General Public License, version 3
# description: bootcdrst - functions needed to build bootcd scripts

err()
{
  echo "ERROR: bootcdrst: $1" >&2
  exit 1
}

# | get_rst_part <name>
# The part starts with undelined <name> in the first column.
# The part continues until a new part starts.
get_rst_part()
{
  local txt
  local name

  name="$1"
  txt="$(cat)"
  if [ "$(echo "$txt" | grep "^${name}$")" ]; then
    txt="$( (echo ; echo "$txt") | sed "1,/^${name}$/d")"
    [ "$(echo "$txt" | head -1 | egrep "^(=|-|~)+$")" ] || err "part <$name> not underlined"
    txt="$( (echo "$txt" ; echo "end"; echo "===") | tail +2 | sed -E -e "/^(=|-|~)+$/,\$d")"
    [ "$(echo "$txt" | tail -1 | egrep "^\S")" ] || err "part <$name> ends with underlined text that does not start at first column."
    txt="$(echo "$txt" | sed "$ d")"
    echo "$txt"
  fi
}

# | rst2txt
rst2txt()
{
  sed "s/\*\*//g" | sed -E -e "s/\*([^*]+)\*/<\1>/g"
}

get_rst_itemlist()
{
  grep "^\S"
}

# | get_rst_item <name>
# The item starts with line "<name>".
# And continues with lines that start with space or empty lines.
get_rst_item()
{
  local txt
  local name
  local srch
  local follow

  name="$1"
  txt="$(cat)"
  srch="$(echo "$name" | sed -E -e "s/\*/\\\*/g" -e "s/\|/\\\\|/g")"
  txt="$( (echo ; echo "$txt") | sed -E -e "1,/^${srch}$/d")"
  follow="$(echo "$txt" | head -1)"
  [ "$(echo "$follow" | sed -n "s/^\s*$/x/p")" ] || err "item <$name> not followed by empty line (srch=<$srch> txt=<$txt> follow=<$follow>)"
  txt="$(echo "$txt" | tail +2 | sed -e "/^\S/,\$d")"
  echo "$txt"
}
#RES="$(/bin/echo -e "  1\n  2\n\n**one**\n\n  3\n  4\n\n**two**\n\n  5\n  6" | get_rst_item "**one**")"
#EXP="$(/bin/echo -e "  3\n  4")"
#[ "$RES" = "$EXP" ] && echo "OK get_rst_item 1" >&2 || echo "ERR get_rst_item 1: EXP=<$EXP> RES=<$RES>" >&2
#RES="$(/bin/echo -e "  1\n  2\n\n**one**\n\n  3\n  4" | get_rst_item "**one**")"
#EXP="$(/bin/echo -e "  3\n  4")"
#[ "$RES" = "$EXP" ] && echo "OK get_rst_item 2" >&2 || echo "ERR get_rst_item 2: EXP=<$EXP> RES=<$RES>" >&2
#RES="$(/bin/echo -e "  1\n  2\n\n**one** *-a|-b|-c*\n\n  3\n  4" | get_rst_item "**one** *-a|-b|-c*")"
#EXP="$(/bin/echo -e "  3\n  4")"
#[ "$RES" = "$EXP" ] && echo "OK get_rst_item 3" >&2 || echo "ERR get_rst_item 3: EXP=<$EXP> RES=<$RES>" >&2
#exit 0

first_paragraph()
{
  sed -e "/^\s*$/,\$ d"
}

# | prefix <pre>
prefix()
{
  pre1="$1"
  pre2="$(echo "$pre1" | sed "s/\s*$//")"
  sed -E -e "s/^(.)/${pre1}\1/" | sed -e "s/^$/${pre2}/"
}

# cat ... | rst2conf
rst2conf()
{
  get_rst_part OPTIONS | rst2txt | prefix "# "
}

# rst2lib_head <name_of_library>
rst2lib_head()
{
  cat <<END
# $1
# vim: set filetype=sh :
#

END
}

# catfile <file>
catfile()
{
  [ -f $1 ] || err "No file $1"
  cat $1
}

# cat ... | rst2lib_printbefore <program>
rst2lib_printbefore()
{
  local txt
  local list
  local i
  local fun

  txt="$(cat | get_rst_part OPTIONS)"
  list="$(echo "$txt" | get_rst_itemlist)"

  echo "$list" | while read i; do
    fun="$(echo "$i" | sed -E -e "s/\*//g" -e "s/(-| )/_/g")"
    cat <<EOF
printbefore_${1}_$fun()
{
  cat <<END
$(echo "$i" | rst2txt | prefix "# ")
#
$(echo "$txt" | get_rst_item "$i" | rst2txt | prefix "# ")
END
}

EOF
  done
}

# rst2lib_defaults <program> <rst> [...]
rst2lib_defaults()
{
  local program
  local rst
  local txt
  local defaults

  program=$1
  shift

  txt="$(
    for rst in $*; do
      cat $rst | get_rst_part OPTIONS
    done
  )"

  defaults="$(
    echo "$txt" | awk '/^\s*Default::/ { s=match($0,/\S/); next }
    { l=match($0,/\S/); if (s>0 && l>0) { if (s<l) { print } else { s=0 } } }'
  )"

  [ "$defaults" ] || defaults="  :"

  cat <<END
defaults_${program}()
{
$defaults
}

END
}

# rst2lib_usage <program>
rst2lib_usage()
{
  local fun
  local usage
  local options

  program="$1"
  txt="$(cat)"

  usage="$(echo "$txt" | get_rst_part SYNOPSIS | rst2txt)"
  options="$(echo "$txt" | get_rst_part OPTIONS)"
  items="$(echo "$options" | get_rst_itemlist)"

  cat <<END
usage_${1}()
{
  cat <<EOF
Usage:$(echo "$usage" | head -1
echo "$usage" | tail +2 | prefix "  ")
$(echo "$items" | while read i; do
  echo
  echo "$i"
  echo "$options" | get_rst_item "$i"
  done | rst2txt | prefix "  ")
EOF
END
  cat <<END
}

END
}

usage()
{
  local err
  eval "$(cat bootcdrst-1.rst | rst2lib_usage bootcdrst)"

  err=0
  if [ "$1" ]; then
    echo "ERROR: $1"
    err=1
  fi
  usage_bootcdrst
  exit $err
}

# option2variable <option> # return name of variable for option
option2variable()
{
  local option
  local variable
  option="$1"
  [ "$option" = "-i|-s|-m|-d debug_runtime_config" ] && return
  [ "$option" = "-i|-s|-m" ] && return
  [ "$option" = "-d debug_runtime_config" ] && return
  [ "$(echo "$option" | grep -e "^--variable_from_")" ] && return
  variable="$(echo "$option" | sed -E -e "s/.*( |-)//")"
  [ ! "$(echo "$variable" | grep "|")" ] || variable="$(echo "$option" | sed -E -e "s/ .*//" -e "s/.*( |-)//")"
  echo "$variable"
}
#RES="$(option2variable "-c|--conf CONF")"
#EXP="CONF"
#[ "$RES" = "$EXP" ] && echo "OK option2variable 1" >&2 || echo "ERR option2variable 1: EXP=<$EXP> RES=<$RES>" >&2
#RES="$(option2variable "-i|-s|-m|-d debug_runtime_config")"
#EXP=""
#[ "$RES" = "$EXP" ] && echo "OK option2variable 2" >&2 || echo "ERR option2variable 2: EXP=<$EXP> RES=<$RES>" >&2
#RES="$(option2variable "-h|--help")"
#EXP="help"
#[ "$RES" = "$EXP" ] && echo "OK option2variable 3" >&2 || echo "ERR option2variable 3: EXP=<$EXP> RES=<$RES>" >&2
#RES="$(option2variable "-z")"
#EXP="z"
#[ "$RES" = "$EXP" ] && echo "OK option2variable 4" >&2 || echo "ERR option2variable 4: EXP=<$EXP> RES=<$RES>" >&2
#RES="$(option2variable "--EFIBOOT bios|efi|bios+efi")"
#EXP="EFIBOOT"
#[ "$RES" = "$EXP" ] && echo "OK option2variable 5" >&2 || echo "ERR option2variable 5: EXP=<$EXP> RES=<$RES>" >&2
#exit 0

# rst2lib_readopts <program-rst> [conf-rst]
rst2lib_readopts()
{
  local v1
  local v2
  local i
  local n
  local txt
  local list
  local name

  name="$(echo "${1}" | sed "s/-.*//")"
  txt="$(catfile ${1} | get_rst_part OPTIONS | sed "s/*//g")"
  list="$(echo "$txt" | get_rst_itemlist)"

  if [ "$2" ]; then
    varnames="$(catfile ${2} | get_rst_part OPTIONS | sed "s/*//g" | get_rst_itemlist)"
    list="$(echo "$list"
      for i in $varnames; do
        echo "--$i $i"
      done)"
    # exp: varnames "VAR1<RETURN>VAR2" -> # list="--VAR1 VAR1<RETURN>--VAR2 VAR2"
  fi

  cat <<END--------
readopts_${name}()
{
END--------
  if [ "$list" ]; then
    echo "$list" | while read i; do
      v1="$(option2variable "$i")"
      [ "$v1" ] || continue
      cat <<END--------
  $v1="\${$v1:=""}"
END--------
    done
  fi
  cat <<END--------
  missing_opt=""
  unknown_opt=""
  while [ \$# -ge 1 ]; do
    case "\$1" in
END--------
      if [ "$list" ]; then
        echo "$list" | while read i; do
          v2="$(option2variable "$i")"
          [ "$v2" ] || continue
          v1="$(echo "$i" | sed -E -e "s/ .*//")"
          n="$(echo "$i" | wc -w)"
          if [ "$n" = "1" ]; then
            cat <<END--------
      $v1) $v2="\$1"; shift ;;
END--------
          else
            cat <<END--------
      $v1) [ \$# -ge 2 ] && $v2="\$2" || ( missing_opt="\$1"; break); shift 2 ;;
END--------
          fi
        done
      fi
      cat <<END--------
      *) unknown_opt="\$1"; break ;;
    esac
  done
}

END--------
}

run_self_test()
{
  exp="$(cat <<END

**bootcdrst** **--run-self-test**|*target file*
*target_file*=**bootcd2disk.conf**|**bootcdconf.lib**|**bootcdwrite.conf**
END
  )"
  res="$(cat bootcdrst-1.rst | get_rst_part "SYNOPSIS")"
  [ "$res" = "$exp" ] && echo "OK get_rst_part 1" || echo "ERR get_rst_part 1 exp=<$exp> res=<$res>"

  exp="$(cat <<END

**--run-self-test**

  Run a test for each function.

  This option is only needed in develpment.

*target file*

  **bootcdrst** can create some files needed by **bootcd**.
  This option creates *target_file*.
  This option is used by Makefile at build time.
END
  )"
  res="$(cat bootcdrst-1.rst | get_rst_part "OPTIONS")"
  [ "$res" = "$exp" ] && echo "OK get_rst_part 2" || echo "ERR get_rst_part 2 exp=<$exp> res=<$res>"

  exp="$(cat <<END

--run-self-test

  Run a test for each function.

  This option is only needed in develpment.

<target file>

  bootcdrst can create some files needed by bootcd.
  This option creates <target_file>.
  This option is used by Makefile at build time.
END
  )"
  res="$(cat bootcdrst-1.rst | get_rst_part "OPTIONS" | rst2txt)"
  [ "$res" = "$exp" ] && echo "OK rst2txt 1" || echo "ERR rst2txt 1 exp=<$exp> res=<$res>"

  exp="$(cat <<END
**--run-self-test**
*target file*
END
  )"
  res="$(cat bootcdrst-1.rst | get_rst_part "OPTIONS" | get_rst_itemlist)"
  [ "$res" = "$exp" ] && echo "OK get_rst_itemlist 1" || echo "ERR get_rst_itemlist 1 exp=<$exp> res=<$res>"

  exp="$(cat <<END
  Run a test for each function.

  This option is only needed in develpment.
END
  )"
  res="$(cat bootcdrst-1.rst | get_rst_part "OPTIONS" | get_rst_item "**--run-self-test**")"
  [ "$res" = "$exp" ] && echo "OK get_rst_item 1" || echo "ERR get_rst_item 1 exp=<$exp> res=<$res>"

  exp="$(cat <<END
  **bootcdrst** can create some files needed by **bootcd**.
  This option creates *target_file*.
  This option is used by Makefile at build time.
END
  )"
  res="$(cat bootcdrst-1.rst | get_rst_part "OPTIONS" | get_rst_item "*target file*")"
  [ "$res" = "$exp" ] && echo "OK get_rst_item 2" || echo "ERR get_rst_item 2 exp=<$exp> res=<$res>"

  exp="$(cat <<END
#
# --run-self-test
#
#   Run a test for each function.
#
#   This option is only needed in develpment.
#
# <target file>
#
#   bootcdrst can create some files needed by bootcd.
#   This option creates <target_file>.
#   This option is used by Makefile at build time.
END
  )"
  res="$(cat bootcdrst-1.rst | rst2conf)"
  [ "$res" = "$exp" ] && echo "OK rst2conf 1" || echo "ERR rst2conf 1 exp=<$exp> res=<$res>"

  exp="$(cat <<EOF
printbefore_bootcdrst___run_self_test()
{
  cat <<END
# --run-self-test
#
#   Run a test for each function.
#
#   This option is only needed in develpment.
END
}

printbefore_bootcdrst_target_file()
{
  cat <<END
# <target file>
#
#   bootcdrst can create some files needed by bootcd.
#   This option creates <target_file>.
#   This option is used by Makefile at build time.
END
}
EOF
  )"
  res="$(cat bootcdrst-1.rst | rst2lib_printbefore bootcdrst)"
  [ "$res" = "$exp" ] && echo "OK rst2lib_printbefore 1" || echo "ERR rst2lib_printbefore 1 exp=<$exp> res=<$res>"

  exp="$(cat <<END
defaults_bootcdrst()
{
  :
}
END
  )"
  res="$(rst2lib_defaults bootcdrst bootcdrst-1.rst)"
  [ "$res" = "$exp" ] && echo "OK rst2lib_defaults 1" || echo "ERR rst2lib_defaults 1 exp=<$exp> res=<$res>"

  exp="usage_bootcdrst()
{
  cat <<EOF
Usage:
  bootcdrst --run-self-test|<target file>
  <target_file>=bootcd2disk.conf|bootcdconf.lib|bootcdwrite.conf

  --run-self-test
    Run a test for each function.

    This option is only needed in develpment.

  <target file>
    bootcdrst can create some files needed by bootcd.
    This option creates <target_file>.
    This option is used by Makefile at build time.
EOF
}"
  res="$(cat bootcdrst-1.rst | rst2lib_usage bootcdrst)"
  [ "$res" = "$exp" ] && echo "OK rst2lib_usage 1" || echo "ERR rst2lib_usage 1 exp=<$exp> res=<$res>"
}

if [ $# -ne 1 ]; then
  usage "need exact 1 argument"
elif [ "$1" = "--run-self-test" ]; then
  run_self_test
elif [ "$1" = "bootcd2disk.conf" ]; then
  catfile bootcd2disk.conf-5.rst | rst2conf
elif [ "$1" = "bootcdconf.lib" ]; then
  rst2lib_head bootcdconf.lib
  catfile bootcdwrite.conf-5.rst | rst2lib_printbefore bootcdwrite
  catfile bootcd2disk.conf-5.rst | rst2lib_printbefore bootcd2disk
  rst2lib_defaults bootcdwrite bootcdwrite.conf-5.rst bootcdwrite-1.rst
  rst2lib_defaults bootcd2disk bootcd2disk.conf-5.rst thisbootcd.conf-5.rst bootcd2disk-1.rst
  rst2lib_defaults bootcdmk2diskconf bootcdmk2diskconf-1.rst
  catfile bootcd-7.rst | rst2lib_usage bootcd
  catfile bootcdwrite-1.rst | rst2lib_usage bootcdwrite
  catfile bootcd2disk-1.rst | rst2lib_usage bootcd2disk
  catfile bootcdmk2diskconf-1.rst | rst2lib_usage bootcdmk2diskconf
  catfile bootcdbackup-1.rst | rst2lib_usage bootcdbackup
  rst2lib_readopts bootcd-7.rst
  rst2lib_readopts bootcdwrite-1.rst bootcdwrite.conf-5.rst
  rst2lib_readopts bootcd2disk-1.rst bootcd2disk.conf-5.rst
  rst2lib_readopts bootcdmk2diskconf-1.rst
  rst2lib_readopts bootcdbackup-1.rst bootcdwrite.conf-5.rst
elif [ "$1" = "bootcdwrite.conf" ]; then
  catfile bootcdwrite.conf-5.rst | rst2conf
else
  usage "unknown option \"$1\""
fi
