#!/bin/sh


# Zerocat Coreboot Machines --- Create very satisfying free software devices.
#
# Copyright (C) 2019, 2020, 2021, 2022  Kai Mertens <kmx@posteo.net>
# Re-exec code suggested by Ricardo Wurmus, 03/2021
#
# This file is part of Zerocat Coreboot Machines.
#
# Zerocat Coreboot Machines is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Zerocat Coreboot Machines is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Zerocat Coreboot Machines.  If not, see <http://www.gnu.org/licenses/>.


# Purpose
# =======
#
# Build seabios payload for platform i386-coreboot.


# Usage
# =====
#
#     ./gen-payload-seabios.sh --usage


# Re-exec if we are not using Bash or are using Bash in POSIX mode.
if [ -z "$BASH" ] || [ "$BASH" = "/bin/sh" ]; then
  bash=`command -v bash`
  if [ -z "$bash" ]; then
    echo "Couldn't find Bash, sorry!"
    exit 1
  else
    exec "$bash" "$0" "$@"
  fi
fi

# We're using Bash now.
set -o errexit
set +o nounset
set -o pipefail

# external sources ###
source ./globals__ANSI-color-escapes.sh
source ./globals__project.sh
source ./lib__debug.sh
source ./lib__get-input.sh
source ./func__print_licheader.sh
source ./func__print_logo.sh

# global constants ###

# global variables ###

# functions ###
usage()
{
  _funcstart "$FUNCNAME"

  _info "Provide usage information ..."
  cat<<EOF

${SET_BOLD}NAME${CLR_INTENSITY}
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} – Generate SeaBIOS payload for platform ${FG_BLUE}i386-coreboot${FG_DEFAULT}.

${SET_BOLD}USAGE${CLR_INTENSITY}
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} [OPTIONS]

${SET_BOLD}OPTIONS${CLR_INTENSITY}
    Note the order of options on the command line should have no importance.

    ${SET_BOLD}--help${CLR_INTENSITY}
    ${SET_BOLD}--usage${CLR_INTENSITY}
        Show usage info.
    ${SET_BOLD}--toolbox${CLR_INTENSITY} <path/to/toolbox-folder>
        Specify a toolbox folder that holds required external projects, e.g. the seabios project.
    <path/to/toolbox-folder>
         This folder should have previously been created by ${SET_BOLD}${TOOLCHAIN_SCRIPT[1]}${CLR_INTENSITY}.
    ${SET_BOLD}--gen-payload${CLR_INTENSITY}
        Compile the SeaBIOS payload.
    ${SET_BOLD}--config-no-vgarom${CLR_INTENSITY}
        Configure SeaBIOS with “CONFIG_NO_VGABIOS=y”. Use this option in case
        you are going to pass an ${SET_UNDERLINE}extracted VGA Option ROM${CLR_UNDERLINE} to ${SET_BOLD}${TOOLCHAIN_SCRIPT[4]}${CLR_INTENSITY}
        via option ${SET_BOLD}--rom-vga${CLR_INTENSITY}.
    ${SET_BOLD}--mute${CLR_INTENSITY}
        Discard standard output of verbose external processes.
        However, their output to stderr will still be visible.
    ${SET_BOLD}--debug${CLR_INTENSITY}
        Enable extra screen output which eases debugging.

${SET_BOLD}EXIT STATUS${CLR_INTENSITY}
    ${SET_BOLD}0${CLR_INTENSITY}
        normal exit
    ${SET_BOLD}1${CLR_INTENSITY}
        an error occurred

${SET_BOLD}EXAMPLES${CLR_INTENSITY}
    Compile SeaBIOS Payload for standard usage with ${SET_BOLD}${TOOLCHAIN_SCRIPT[4]}${CLR_INTENSITY}:
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} \\
        ${SET_BOLD}--toolbox${CLR_INTENSITY} <path/to/toolbox-folder> \\
        ${SET_BOLD}--gen-payload${CLR_INTENSITY}

    Compile SeaBIOS Payload for usage with “${SET_BOLD}${TOOLCHAIN_SCRIPT[4]} --rom-vga${CLR_INTENSITY} <vga.rom>”:
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} \\
        ${SET_BOLD}--toolbox${CLR_INTENSITY} <path/to/toolbox-folder> \\
        ${SET_BOLD}--gen-payload${CLR_INTENSITY} \\
        ${SET_BOLD}--config-no-vgarom${CLR_INTENSITY}

EOF
  _pass "... usage information has been provided."

  _funcstop "$FUNCNAME"
}

parse_cmdline()
{
  _funcstart "$FUNCNAME"

  declare -i n=
  declare opt=

  _info "Parse command line ..."
  while [ $# -gt 0 ]; do
    opt=${1:?missing parameter}
    n=1
    opt=${opt,,}
    case "${opt}" in
      # parameters
      '--debug')
        # no action here, see bottom of script
        ;;
      '--mute')
        opt__mute="/dev/null"
        ;;
      '--toolbox')
        [ "$arg__toolbox" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--toolbox: duplicate option"
        [ "${2:0:2}" = '--' ] || [ "${2}" = '' ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--toolbox: missing argument"
        opt=$2
        n+=1
        pushd "${opt}" &>/dev/null ||
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--toolbox: no such directory: ${opt}"
        arg__toolbox=$(dirs -l +0)
        popd &>/dev/null
        path_to_seabios=$(echo "${arg__toolbox}"/seabios[-@${FVS}]*/)
        select_option path_to_seabios
        [ -d "$path_to_seabios" ] ||
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--toolbox: no such directory: "$path_to_seabios
        version_seabios="${path_to_seabios##*\/seabios[-@${FVS}]}"
        version_seabios="${version_seabios%\/}"
        ;;
      '--config-no-vgarom')
        opt__config_no_vgarom=1
        ;;
      # jobs
      '--help')
        ;&
      '--usage')
        opt__usage=1
        ;;
      '--gen-payload')
        opt__gen_payload=1
        ;;
      *)
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "unknown option: ${opt}"
        ;;
    esac
    shift $n
  done
  if [ $# -ne 0 ]; then
    _warn "... $# parameters were skipped."
  else
    _pass "... command line parsed."
  fi

  _funcstop "$FUNCNAME"
}

# $1: config file
config_NO_VGABIOS()
{
  _funcstart "$FUNCNAME"

  _info "Configure SeaBIOS CONFIG_NO_VGABIOS ..."
  [ -f "$1" ] ||
    go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "no such file: $1"

  case "$version_seabios" in
    (*)
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "unsupported seabios version"
      ;;
  esac
  _pass "... SeaBIOS CONFIG_NO_VGABIOS has been set."

  _funcstop "$FUNCNAME"
}

job_list()
{
  _funcstart "$FUNCNAME"

  declare retval=1

  # jobs
  _dbug "${FG_MAGENTA}Jobs ...${FG_DEFAULT}"
  _info "Requested jobs are:"
  [ "$opt__usage" ] && {
    _mesg "${LIITEM}display usage information"
    retval=0
  }
  [ "$opt__gen_payload" ] && {
    _mesg "${LIITEM}compile SeaBIOS"
    retval=0
  }
  [ "$retval" -eq 0 ] ||
    _mesg "${INDENT}none"

  _funcstop "$FUNCNAME"
  return "$retval"
}

check_options()
{
  _funcstart "$FUNCNAME"

  _info "Check options ..."

  # --debug

  # --mute

  # --toolbox

  # --config-no-vgarom
  [ "$opt__config_no_vgarom" ] && {
    [ "$arg__toolbox" ] && [ "$opt__gen_payload" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-no-vgarom: incomplete set of command line options"
  }

  # --usage | --help

  # --gen-payload
  [ "$opt__gen_payload" ] && {
    [ "$arg__toolbox" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--gen-payload: incomplete set of command line options"
  }

  _pass "... options checked."

  _funcstop "$FUNCNAME"
}

check_settings()
{
  _funcstart "$FUNCNAME"

  _info "Check settings ..."

  # --debug
  [ "$DEBUG" -eq 0 ] ||
    _mesg "Debug output is activated."

  # --mute
  [ "$opt__mute" ] &&
    _mesg "Standard output of external processes is muted."

  # --toolbox
  [ "$arg__toolbox" ] && {
    _mesg "Toolbox Folder:${RTAB}${arg__toolbox}"
    _mesg "${LIITEM}Path to SeaBIOS:${RTAB}${path_to_seabios}"
    _dbug "${LIITEM}SeaBIOS Version:${RTAB}${version_seabios}"
    _dbug "${LIITEM}Config Template:${RTAB}${TEMPLATE_SEABIOS_CONFIG/\$\{version_seabios\}/$version_seabios}"
  }

  # --config-no-vgarom
  [ "$opt__config_no_vgarom" ] &&
    _mesg "SeaBIOS VGA BIOS:${RTAB}none"

  _pass "... settings checked."

  _funcstop "$FUNCNAME"
}

parse_debug()
{
  declare i=

  DEBUG=0
  for i in "$@"; do
    [ "$i" = '--debug' ] &&
      DEBUG=1
  done

  true
}

main()
{
  trap '
    echo
    go_exit "$BASH_SOURCE" "$FUNCNAME" "" "$BASH_COMMAND"
    ' SIGINT ERR   # what sigspec is related to ERR??

  _funcstart "$FUNCNAME"

  declare -r scriptname="${TOOLCHAIN_SCRIPT[2]}"
  declare osid=
  declare -r -a commands_script=(
    'source'
    'echo'
    'printf'
    'cat'
    'sed'
    'rm'
    'mv'
    'cd'
    'sleep'
    'exit'
    'make'
  )

  declare opt__mute=
  declare opt__usage=
  declare opt__gen_payload=
  declare opt__config_no_vgarom=

  declare arg__toolbox=

  declare path_to_seabios=        # directly related to $arg__toolbox
  declare version_seabios=

  _head "Zerocat Coreboot Machines ${PROJECT_NUMBER}”"
  _head "$scriptname – Generate SeaBIOS Payload"
  print_licheader
  print_logo
  test_scriptname "$BASH_SOURCE" "$scriptname" && {
    get_OSID 'osid'
    check_commands 'commands_script'
    _info "$(date), ready to start."
    hit_enter
    parse_cmdline "$@"
    check_options
    check_settings
    job_list && {
      yes_no_abort && {
        [ "${opt__usage}" ] && {
          usage
        }
        [ "${opt__gen_payload}" ] && {
          _info "Copy SeaBIOS configuration template ..."
          echo -e -n "${SET_DIM}" &&
            cp \
              ${TEMPLATE_SEABIOS_CONFIG/\$\{version_seabios\}/$version_seabios} \
              "${SEABIOS_CONFIG/\$\{path_to_seabios\}/$path_to_seabios}" \
              1>"${opt__mute:-/dev/stdout}" ||
                go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
          _pass "... configuration template copied."
          [ "$opt__config_no_vgarom" ] && {
            _info "Patching configuration ..."
            echo -e -n "${SET_DIM}" &&
              patch \
                "${SEABIOS_CONFIG/\$\{path_to_seabios\}/$path_to_seabios}" \
                "./seabios@rel-1.14.0-no-vgarom.patch" \
                  1>"${opt__mute:-/dev/stdout}" ||
                    go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
            _pass "... configuration patched."
          }
          _info "Compile SeaBIOS Payload ..."
          if [ "${path_to_seabios//[^ ]}" != '' ]; then
            _warn "The SeaBIOS Makefile is likely to fail with paths containing white space."
            _mesg "Check messages!"
          fi
          echo -e -n "${SET_DIM}" && {
            pushd "${path_to_seabios:?is not set}" &>/dev/null ||
              go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
            # Note with paths containing white space, make is likely to fail with exit status zero.
            # Check make’s output!
            CC="$(command -v gcc)" make --print-directory clean 1>"${opt__mute:-/dev/stdout}" ||
              go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "‘make clean’ failed"
            CC="$(command -v gcc)" make --print-directory 1>"${opt__mute:-/dev/stdout}" ||
              go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "‘make’ failed"
            popd &>/dev/null
          }
          _pass "... SeaBIOS Payload successfully compiled."
          _mesg "For results, please check folder:"
          _mesg "${INDENT}${path_to_seabios}/out/"
        }
      }
    }
  }
  _info "Good Bye."

  _funcstop "$FUNCNAME"

  trap - ERR SIGINT
}

# script
parse_debug "$@"
main "$@"
