#!/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
# =======
#
# Generate grub exectable *.elf payload file for platform i386-coreboot.


# Usage
# =====
#
#     ./gen-payload-grub.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
usage()
{
  _funcstart "$FUNCNAME"

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

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

${SET_BOLD}USAGE${CLR_INTENSITY}
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} \\
        [<Help-Options>] \\
        [<Screen-Output-Control>]
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} \\
        [<Payload-Folder-Options>] \\
        [<Clean-Options>] \\
        [<Screen-Output-Control>]
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} \\
        [<Toolbox-Options>] \\
        [<Payload-Folder-Options>] \\
        [<Clean-Options>] \\
        [<Payload-Options>] \\
        [<Screen-Output-Control>]

${SET_BOLD}OPTIONS${CLR_INTENSITY}
    Note the order of options on the command line should have no importance, as the order of job execution
    is hardcoded. Use ${SET_BOLD}--clean${CLR_INTENSITY} to clean the payload folder as a precondition, but use
    ${SET_BOLD}--mostlyclean${CLR_INTENSITY} in case you intend to re-use a previously generated *.elf payload file.

    ${SET_BOLD}<Help-Options>${CLR_INTENSITY}
        ${SET_BOLD}--help${CLR_INTENSITY}
        ${SET_BOLD}--usage${CLR_INTENSITY}
            Show usage information.

    ${SET_BOLD}<Screen-Output-Control>${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}<Toolbox-Options>${CLR_INTENSITY}
        ${SET_BOLD}--toolbox${CLR_INTENSITY} <path/to/toolbox-folder>
            Specify a toolbox folder that holds required external projects.

        <path/to/toolbox-folder>
            This folder should have previously been created by ${SET_BOLD}${TOOLCHAIN_SCRIPT[1]}${CLR_INTENSITY}.

    ${SET_BOLD}<Payload-Folder-Options>${CLR_INTENSITY}
        ${SET_BOLD}--payload-dir${CLR_INTENSITY} <path/to/grub-payload-folder>
            Specify a payload folder that holds generated output such as executable *.elf payload files,
            configuration files, snippets and GRUB2 console keymaps.

        <folder>
            Output directory for the GRUB2 payload. This folder is to be passed to ${SET_BOLD}${TOOLCHAIN_SCRIPT[4]}${CLR_INTENSITY}
            via ${SET_BOLD}--payload-grub-dir${CLR_INTENSITY}.

    ${SET_BOLD}<Payload-Options>${CLR_INTENSITY}
        ${SET_BOLD}<Keymap-Options>${CLR_INTENSITY}
            ${SET_BOLD}--gen-keymaps${CLR_INTENSITY}
                Generate keyboard layout files *.gkb and create appropriate <Snippet-Files>.
            ${SET_BOLD}--show-snippets${CLR_INTENSITY}
                Display generated <Snippet-Files> on ${SET_BOLD}stdout${CLR_INTENSITY}.

        ${SET_BOLD}<ELF-Options>${CLR_INTENSITY}
            ${SET_BOLD}--gen-payload${CLR_INTENSITY}
                Generate a small GRUB executable *.elf payload file (default) for platform i386-coreboot:
                ${GRUB_PAYLOAD}
            ${SET_BOLD}--gen-payload-extended${CLR_INTENSITY}
                Generate a full GRUB executable *.elf payload file (experimental) for platform i386-coreboot:
                ${GRUB_PAYLOAD_EXTENDED}

        ${SET_BOLD}<Config-Options>${CLR_INTENSITY}
            ${SET_BOLD}--gen-config${CLR_INTENSITY}
                Generate GRUB configuration files, made from templates and code-snippets.
                Default: ${TEMPLATE_GRUB_CONFIG##*\/}
            ${SET_BOLD}--config-keymap${CLR_INTENSITY} <layout>
                Specify a valid keymap layout, as like us, gb, de, etc.
                See ${SET_BOLD}${SNIPPET_COMMENTS}${CLR_INTENSITY} to see valid layouts.
                If no layout other than the default (us) is configured, you might still select
                your layout via GRUB’s Boot Menu.
            ${SET_BOLD}--config-default${CLR_INTENSITY} <number>
                Specify a valid, default menu entry, as like ${SET_BOLD}0${CLR_INTENSITY} (the default) or ${SET_BOLD}1${CLR_INTENSITY}, for instance.
                Parameter <number> denotes a menu entry of the ${SET_UNDERLINE}resulting${CLR_UNDERLINE} config file, counting starts with zero.
                ${SET_UNDERLINE}Please verify that your value points to the intended menu entry!${CLR_UNDERLINE}
            ${SET_BOLD}--config-tint${CLR_INTENSITY}
                This will add menu entry ‘Tint Game’ to the Boot Menu of the generated GRUB
                configuration file. Note payload ‘Tint’ is available for targets x60_32bit, only.
            ${SET_BOLD}--config-memtest86+${CLR_INTENSITY}
                This will add menu entry ‘Memtest86+’ to the Boot Menu of the generated GRUB
                configuration file. Note payload ‘Memtest86+’ is available for targets x60_32bit, only.
            ${SET_BOLD}--config-timeout${CLR_INTENSITY}
                Enable timeout.
            ${SET_BOLD}--config-seabios${CLR_INTENSITY}
                This will add menu entry ‘SeaBIOS’ to the Boot Menu of the generated GRUB
                configuration file.
            ${SET_BOLD}--config-morse${CLR_INTENSITY}
                This will add menu entry ‘Toggle Morse Feedback On/Off’ to the Boot Menu of the
                generated GRUB configuration file. Note on some hardware, GRUB@2.02 supports audio output
                and you might consider to use this option.
            ${SET_BOLD}--config-authorization${CLR_INTENSITY} <user> <password> <scheme>
                Activate authorization support within the GRUB Boot Menu.

                <user>
                    Specify a user name, such as ‘grubuser’ or ‘user0’
                <password>
                    Specify the user’s password (clear text) or password hash (grub_pbkdf2).
                <scheme>
                    Specify the authorization scheme, such as:

                    ${SET_BOLD}default${CLR_INTENSITY}
                        Access to hotkeys ‘c’, ‘e’ and ‘q’ is restricted, whereas access to menu entries is
                        unrestricted.

                    ${SET_BOLD}basic${CLR_INTENSITY}
                        Access to hotkeys ‘c’, ‘e’ and ‘q’ is restricted, same as to selected menu entries.

                    ${SET_BOLD}advanced${CLR_INTENSITY}
                        Access to all entries and all hotkeys but ‘h’ and ‘x’ is restricted.

            ${SET_BOLD}--config-background${CLR_INTENSITY} <image.[png|PNG|jpg|JPG|jpeg|JPEG]>
                This option creates a symbolic link to the specified background image.
                The link name will be placed in the payload folder, where it will be automatically picked
                from by script ${SET_BOLD}${TOOLCHAIN_SCRIPT[4]}${CLR_INTENSITY}. This option might be used up to four times.
            ${SET_BOLD}--config-coreinfo${CLR_INTENSITY}
                This will add menu entry ‘Coreinfo’ to the Boot Menu of the generated GRUB
                configuration file. Note payload ‘Coreinfo’ freezes on T60 machines.
            ${SET_BOLD}--config-nvramcui${CLR_INTENSITY}
                This will add menu entry ‘NVRAMCUI’ to the Boot Menu of the generated GRUB
                configuration file.
            ${SET_BOLD}--config-antifreeze${CLR_INTENSITY}
                This will add a kernel boot option ‘intel_idle.max_cstate=2’ to menu entry ‘Search Kernel Linux’ to address
                freezes on X220 and T520 machines.
            ${SET_BOLD}--config-no-poweroff${CLR_INTENSITY}
                This will remove menu entry ‘Power off’ from the Boot Menu of the generated GRUB
                configuration file. Note the related GRUB command ‘halt’ sometimes doesn’t work
                on T60 machines, for instance.
            ${SET_BOLD}--strip-comments${CLR_INTENSITY}
                Strip comments and white space from generated configuration files.

    ${SET_BOLD}<Clean-Options>${CLR_INTENSITY}
        ${SET_BOLD}--clean${CLR_INTENSITY}
            Same as ${SET_BOLD}--mostlyclean${CLR_INTENSITY}, but furthermore remove generated *.elf and
            <Snippet-Files> from payload folder.
        ${SET_BOLD}--mostlyclean${CLR_INTENSITY}
            In the payload folder, clean *.cfg and links to background images, but keep *.elf and <Snippet-Files>.
        ${SET_BOLD}--remove${CLR_INTENSITY}
            remove the payload folder, if empty. Content should have been cleaned away with option ${SET_BOLD}--clean${CLR_INTENSITY}, first.

${SET_BOLD}FILES${CLR_INTENSITY}
    ${SET_BOLD}<Snippet-Files>${CLR_INTENSITY}
        \${arg__payload_dir}/${SNIPPET_MAPS}
            List of generated keymap files, used by ${SET_BOLD}--clean${CLR_INTENSITY}.
        \${arg__payload_dir}/${SNIPPET_GRAFTS}
            Code snippet for the invocation of ${SET_BOLD}grub-mkstandalone${CLR_INTENSITY}, used by this script.
        \${arg__payload_dir}/${SNIPPET_COMMENTS}
            Configuration snippet, listing all generated keymap layouts and a statement
            defaulting to ‘keymap ${KEYMAP_DEFAULT}’.

    ${SET_BOLD}<Required-Templates>${CLR_INTENSITY}
        ${TEMPLATE_GRUB_MODULES_INSTALL}
        ${TEMPLATE_GRUB_CONFIG_MEMDISK}
        ${TEMPLATE_GRUB_CONFIG}
        ${TEMPLATE_GRUB_CONFIG_MORSE}

    ${SET_BOLD}<Payload-Files>${CLR_INTENSITY}
        \${arg__payload_dir}/${GRUB_PAYLOAD}
        \${arg__payload_dir}/${GRUB_PAYLOAD_EXTENDED}
        \${arg__payload_dir}/${TEMPLATE_GRUB_CONFIG##*\/}
        \${arg__payload_dir}/${TEMPLATE_GRUB_CONFIG_MORSE##*\/}

${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}
    Generate a GRUB Payload File for platform i386-coreboot, generic:
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} \\
        ${SET_BOLD}--toolbox${CLR_INTENSITY} <path/to/toolbox-folder> \\
        ${SET_BOLD}--payload-dir${CLR_INTENSITY} <path/to/grub-payload-folder> \\
        ${SET_BOLD}--clean${CLR_INTENSITY} \\
        ${SET_BOLD}--gen-keymaps${CLR_INTENSITY} \\
        ${SET_BOLD}--gen-payload${CLR_INTENSITY}

    Generate GRUB Configuration Files, generic:
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} \\
        ${SET_BOLD}--toolbox${CLR_INTENSITY} <path/to/toolbox-folder> \\
        ${SET_BOLD}--payload-dir${CLR_INTENSITY} <path/to/grub-payload-folder> \\
        ${SET_BOLD}--mostlyclean${CLR_INTENSITY} \\
        ${SET_BOLD}--gen-config${CLR_INTENSITY} \\
        ${SET_BOLD}--config-seabios${CLR_INTENSITY} \\
        ${SET_BOLD}--config-nvramcui${CLR_INTENSITY} \\
        ${SET_BOLD}--config-coreinfo${CLR_INTENSITY} \\
        ${SET_BOLD}--config-authorization grubuser password123 default${CLR_INTENSITY} \\
        ${SET_BOLD}--strip-comments${CLR_INTENSITY}

    Generate GRUB Configuration Files, dedicated for X60 targets, 32bit CPU:
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} \\
        ${SET_BOLD}--toolbox${CLR_INTENSITY} <path/to/toolbox-folder> \\
        ${SET_BOLD}--payload-dir${CLR_INTENSITY} <path/to/grub-payload-folder> \\
        ${SET_BOLD}--mostlyclean${CLR_INTENSITY} \\
        ${SET_BOLD}--gen-config${CLR_INTENSITY} \\
        ${SET_BOLD}--config-seabios${CLR_INTENSITY} \\
        ${SET_BOLD}--config-nvramcui${CLR_INTENSITY} \\
        ${SET_BOLD}--config-coreinfo${CLR_INTENSITY} \\
        ${SET_BOLD}--config-memtest86+${CLR_INTENSITY} \\
        ${SET_BOLD}--config-tint${CLR_INTENSITY} \\
        ${SET_BOLD}--config-morse${CLR_INTENSITY} \\
        ${SET_BOLD}--config-authorization grubuser password123 default${CLR_INTENSITY} \\
        ${SET_BOLD}--strip-comments${CLR_INTENSITY}

    Generate GRUB Configuration Files, dedicated for T60 targets:
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} \\
        ${SET_BOLD}--toolbox${CLR_INTENSITY} <path/to/toolbox-folder> \\
        ${SET_BOLD}--payload-dir${CLR_INTENSITY} <path/to/grub-payload-folder> \\
        ${SET_BOLD}--mostlyclean${CLR_INTENSITY} \\
        ${SET_BOLD}--gen-config${CLR_INTENSITY} \\
        ${SET_BOLD}--config-seabios${CLR_INTENSITY} \\
        ${SET_BOLD}--config-no-poweroff${CLR_INTENSITY} \\
        ${SET_BOLD}--config-authorization grubuser password123 default${CLR_INTENSITY} \\
        ${SET_BOLD}--strip-comments${CLR_INTENSITY}

    Generate GRUB Configuration Files, dedicated for T520 targets:
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} \\
        ${SET_BOLD}--toolbox${CLR_INTENSITY} <path/to/toolbox-folder> \\
        ${SET_BOLD}--payload-dir${CLR_INTENSITY} <path/to/grub-payload-folder> \\
        ${SET_BOLD}--mostlyclean${CLR_INTENSITY} \\
        ${SET_BOLD}--gen-config${CLR_INTENSITY} \\
        ${SET_BOLD}--config-antifreeze${CLR_INTENSITY} \\
        ${SET_BOLD}--config-seabios${CLR_INTENSITY} \\
        ${SET_BOLD}--config-nvramcui${CLR_INTENSITY} \\
        ${SET_BOLD}--config-coreinfo${CLR_INTENSITY} \\
        ${SET_BOLD}--config-authorization grubuser password123 default${CLR_INTENSITY} \\
        ${SET_BOLD}--strip-comments${CLR_INTENSITY}

    Generate link names to selected background images, a Zerocat Background for the x230, for instance:
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} \\
        ${SET_BOLD}--payload-dir${CLR_INTENSITY} <path/to/grub-payload-folder> \\
        ${SET_BOLD}--config-background <path/to/image.png>${CLR_INTENSITY}

    Clean and remove a Payload folder:
    ${SET_BOLD}$BASH_SOURCE${CLR_INTENSITY} \\
        ${SET_BOLD}--payload-dir${CLR_INTENSITY} <path/to/grub-payload-folder> \\
        ${SET_BOLD}--clean${CLR_INTENSITY} \\
        ${SET_BOLD}--remove${CLR_INTENSITY}

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

  _funcstop "$FUNCNAME"
}

# create console keymaps and set snippet variables
gen_keymaps()
{
  _funcstart "$FUNCNAME"

  declare -r -a language=( ''  de  ''  ''  fr  ''  en  ''  de  ''  ''  ''  de  ''  ''  ''  ''  fr  fr      en  en      en   ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''      ''  ''  ''  ''  ''  en  en      en    ''  ''  )
  declare -r -a  country=( AL  AT  AZ  BA  BE  BR  CA  CD  CH  CM  CN  CZ  DE  DK  EE  ES  FO  FR  FR      GB  GB      GB   GH  HR  HU  IS  IT  JP  KE  KR  LT  LV  MD  ME  ML  MT  NG  NL  NO  PH  PL  PT  RO  SE  SI  SK  SK      SN  TH  TM  TR  TR  US  US      US    VN  ZA  )
  declare -r -a  variant=( ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  latin9  ''  dvorak  intl ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  ''  qwerty  ''  ''  ''  ''  f   ''  dvorak  intl  ''  ''  )
  declare -i i
  declare n
  declare map

  _info "Generate console keymaps ..."
  for (( i=0; i<${#country[@]}; i++ )) ; do
    map="${country[$i],,}${variant[$i]:+_}${variant[$i]}"
    n="$(echo "${map:0:1}" | tr 'a-e' '0' | tr 'f-l' '1' | tr 'm-s' '2' | tr 't-z' '3')"
    _mesg "Create console keymap ${map}.gkb ..."
    echo -e -n "${SET_DIM}" &&
      ckbcomp \
        -I"$GUIX_ENVIRONMENT/share/X11/xkb" \
        -rules "base" \
        -layout "${country[$i],,}" \
        -variant "${variant[$i]}" |
          "${bin__grub_mklayout:?is not set}" -o "${map}.gkb" 1>"${opt__mute:-/dev/stdout}" && {
            snippet_comments[n]+="${map} "
            snippet_maps[n]+="${map}.gkb "
            snippet_grafts+="/boot/grub/layouts/${map}.gkb=${arg__payload_dir}/${map}.gkb \\"$'\n'
            _mesg "... keymap created:${RTAB}${map}.gkb"
          } ||
            go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  done
  gen_snippet_maps
  gen_snippet_grafts "$snippet_grafts"
  gen_snippet_comments
  _pass "... console keymaps generated."

  _funcstop "$FUNCNAME"
}

gen_snippet_maps()
{
  _funcstart "$FUNCNAME"

  _mesg "Create snippet list of generated keymap files ..."
  echo -n > $SNIPPET_MAPS
  for (( i=0; i<${#snippet_maps[@]}; i++ )); do
    echo -e "${snippet_maps[$i]}\\ " >> $SNIPPET_MAPS
  done
  _mesg "... file created:${RTAB}$SNIPPET_MAPS"

  _funcstop "$FUNCNAME"
}

# suggest code snippet for GRUB configuration file
gen_snippet_comments()
{
  _funcstart "$FUNCNAME"

  _mesg "Create keymap configuration snippet file ..."
  echo "# Keymap --- Several options available, pick your layout:" > $SNIPPET_COMMENTS
  for (( i=0; i<${#snippet_comments[@]}; i++ )); do
    echo -e "# ${snippet_comments[$i]}" >> $SNIPPET_COMMENTS
  done
  echo "keymap ${KEYMAP_DEFAULT}" >> $SNIPPET_COMMENTS
  _mesg "... file created:${RTAB}$SNIPPET_COMMENTS"

  _funcstop "$FUNCNAME"
}

# $1: list of grafts as in $snippet_grafts
gen_snippet_grafts()
{
  _funcstart "$FUNCNAME"

  _mesg "Create snippet file of grafts ..."
  echo "$(echo ${1:?is not set} | sed -r -e 's#\\##g; s#  # #g;' - )" > $SNIPPET_GRAFTS
  _mesg "... file created:${RTAB}$SNIPPET_GRAFTS"

  _funcstop "$FUNCNAME"
}

# $1: snippet file to be displayed
display_snippet()
{
  _funcstart "$FUNCNAME"

  _info "Display snippet file $1 ..."
  if [ -f "$1" ]; then
    echo -e -n "${SET_DIM}" &&
      cat<<EOF

---------8<------------cut here cut here cut here------------>8---------
$(cat ${1:?is not set})
---------8<------------cut here cut here cut here------------>8---------

EOF
    _pass "... file displayed:${RTAB}$1"
  else
    _fail "No such file:${RTAB}$1"
  fi

  _funcstop "$FUNCNAME"
}

remove()
{
  _funcstart "$FUNCNAME"

  _info "Remove payload folder ..."
  echo -e -n "${SET_DIM}" &&
    rmdir $arg__payload_dir
  if [ $? -ne 0 ]; then
    _fail "... payload folder is not empty."
  else
    _pass "... payload folder removed."
  fi

  _funcstop "$FUNCNAME"
}

# $@:  files to remove
clean()
{
  _funcstart "$FUNCNAME"

  declare -a files=()
  declare i=
  declare -i n=

  n=0
  for i in $@; do
    [ -f "$i" ] && {
      files[n]="$i"
      n+=1
    }
  done
  [[ "${#files[@]}" -eq 0 ]] || {
    _warn "This will remove files ..."
    for i in "${files[@]}"; do
      _mesg "· $i"
    done
    yes_no_abort && {
      rm -f "${files[@]}"
      _pass "... files removed."
    }
  }

  _funcstop "$FUNCNAME"
}

gen_payload_extended()
{
  _funcstart "$FUNCNAME"

  _info "Create extended, memdisk-based GRUB2 payload for Coreboot ..."
  # themes, locales and available fonts
  echo -e -n "${SET_DIM}" &&
    ${bin__grub_mkstandalone:?is not set} \
      --directory="${path_to_grub}/out/lib/grub/i386-coreboot" \
      --output="${arg__payload_dir}/${file_elf_extended}" \
      --format='i386-coreboot' \
      --verbose \
      --fonts='unicode ascii' \
      --themes='starfield' \
      --install-modules="${modules_install}" \
      --modules='normal test cbfs' \
      /boot/grub/grub.cfg="${PATH_TO_WD}/${file_memdisk_config}" \
      $(cat ${arg__payload_dir}/${SNIPPET_GRAFTS}) \
      1>"${opt__mute:-/dev/stdout}" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  _pass "... GRUB2 payload – extended – created:"
  _mesg "${INDENT}${arg__payload_dir}/${file_elf_extended}"

  _funcstop "$FUNCNAME"
}

gen_payload()
{
  _funcstart "$FUNCNAME"

  _info "Create standard, memdisk-based GRUB2 payload for Coreboot ..."
  # no themes, no locales, just unifont font.
  echo -e -n "${SET_DIM}" &&
    "${bin__grub_mkstandalone:?is not set}" \
      --directory="${path_to_grub}/out/lib/grub/i386-coreboot" \
      --output="${arg__payload_dir}/${file_elf}" \
      --format='i386-coreboot' \
      --verbose \
      --fonts='unicode' \
      --themes= \
      --locales= \
      --install-modules="${modules_install}" \
      --modules='normal test cbfs' \
      /boot/grub/grub.cfg="${PATH_TO_WD}/${file_memdisk_config}" \
      $(cat ${arg__payload_dir}/${SNIPPET_GRAFTS}) \
      1>"${opt__mute:-/dev/stdout}" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  _pass "... GRUB2 payload – standard – created:"
  _mesg "${INDENT}${arg__payload_dir}/${file_elf}"

  _funcstop "$FUNCNAME"
}

check_options()
{
  _funcstart "$FUNCNAME"

  _info "Check options ..."

  # --debug

  # --mute

  # --config-default
  [ "$arg__config_default" ] && {
    [ "$arg__payload_dir" -a "$opt__gen_config" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-default: incomplete set of command line options"
  }

  # --config-keymap
  [ "$opt__grub_keymap" ] && {
    [ "$arg__payload_dir" -a "$opt__gen_config" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-keymap: incomplete set of command line options"
  }

  # --config-background
  [ "$opt__config_background" ] && {
    [ "$arg__payload_dir" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-background: incomplete set of command line options"
  }

  # --config-seabios
  [ "$opt__config_seabios" ] && {
    [ "$arg__payload_dir" -a "$opt__gen_config" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-seabios: incomplete set of command line options"
  }

  # --config-tint
  [ "$opt__config_tint" ] && {
    [ "$arg__payload_dir" -a "$opt__gen_config" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-tint: incomplete set of command line options"
  }

  # --config-memtest86+
  [ "$opt__config_memtest86" ] && {
    [ "$arg__payload_dir" -a "$opt__gen_config" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-memtest86+: incomplete set of command line options"
  }

  # --config-coreinfo
  [ "$opt__config_coreinfo" ] && {
    [ "$arg__payload_dir" -a "$opt__gen_config" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-coreinfo: incomplete set of command line options"
  }

  # --config-nvramcui
  [ "$opt__config_nvramcui" ] && {
    [ "$arg__payload_dir" -a "$opt__gen_config" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-nvramcui: incomplete set of command line options"
  }

  # --config-no-poweroff
  [ "$opt__config_no_poweroff" ] && {
    [ "$arg__payload_dir" -a "$opt__gen_config" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-no-poweroff: incomplete set of command line options"
  }

  # --config-morse
  [ "$opt__config_morse" ] && {
    [ "$arg__payload_dir" -a "$opt__gen_config" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-morse: incomplete set of command line options"
  }

  # --config-antifreeze
  [ "$opt__config_antifreeze" ] && {
    [ "$arg__payload_dir" -a "$opt__gen_config" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-antifreeze: incomplete set of command line options"
  }

  # --toolbox

  # --payload-dir

  # --help | --usage

  # --remove
  [ "$opt__remove" ] && {
    [ "$arg__payload_dir" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--remove: incomplete set of command line options"
  }

  # --mostlyclean
  [ "$opt__mostlyclean" ] && {
    [ "$arg__payload_dir" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--mostlyclean: incomplete set of command line options"
  }

  # --clean
  [ "$opt__clean" ] && {
    [ "$arg__payload_dir" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--clean: incomplete set of command line options"
  }

  # --gen-keymaps
  [ "$opt__gen_keymaps" ] && {
    check_commands 'commands__gen_keymaps'
    [ "$arg__payload_dir" -a "$arg__toolbox" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--gen-keymaps: incomplete set of command line options"
  }

  # --show-snippets
  [ "$opt__show_snippets" ] && {
    [ "$arg__payload_dir" -a "$opt__gen_keymaps" ] ||
      [ -f "${arg__payload_dir}/$SNIPPET_GRAFTS" -a -f "${arg__payload_dir}/$SNIPPET_COMMENTS" ] ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--show-snippets: incomplete set of command line options"
  }

  # --gen-payload-extended
  [ "$opt__gen_payload_extended" ] && {
    [ "$arg__payload_dir" -a "$arg__toolbox" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--gen-payload-extended: incomplete set of command line options"
    [ "$opt__gen_keymaps" -o -f "${arg__payload_dir}/$SNIPPET_GRAFTS" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--gen-payload-extended: incomplete set of command line options"
  }

  # --gen-payload
  [ "$opt__gen_payload" ] && {
    [ "$arg__payload_dir" -a "$arg__toolbox" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--gen-payload: incomplete set of command line options"
    [ "$opt__gen_keymaps" -o -f "${arg__payload_dir}/$SNIPPET_GRAFTS" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--gen-payload: incomplete set of command line options"
  }

  # --gen-config
  [ "$opt__gen_config" ] && {
    [ "$arg__payload_dir" -a "$arg__toolbox" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--gen-config: incomplete set of command line options"
    [ "$opt__gen_keymaps" -o -f "${arg__payload_dir}/${SNIPPET_COMMENTS}" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--gen-config: incomplete set of command line options"
  }

  # --strip-comments
  [ "$opt__strip_comments" ] && {
    [ "$arg__payload_dir" -a "$arg__toolbox" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--strip-comments: please use in combination with --toolbox"
  }

  _pass "... options checked."

  _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_grub=$(echo "${arg__toolbox}"/grub[-@${FVS}]*/)
        select_option path_to_grub
        [ -d "$path_to_grub" ] ||
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "no such directory: $path_to_grub"
        bin__grub_mklayout="${path_to_grub}/out/bin/grub-mklayout"
        bin__grub_mkstandalone="${path_to_grub}/out/bin/grub-mkstandalone"
        version_grub="${path_to_grub##*\/grub[-@${FVS}]}"
        version_grub="${version_grub%\/}"
        file_elf="${GRUB_PAYLOAD/\$\{version_grub\}/${version_grub}}"
        file_elf_extended="${GRUB_PAYLOAD_EXTENDED/\$\{version_grub\}/${version_grub}}"
        file_memdisk_config="${TEMPLATE_GRUB_CONFIG_MEMDISK/\$\{version_grub\}/${version_grub}}"
        [ -f "$file_memdisk_config" ] ||
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "no such file: $file_memdisk_config"
        template_grub_config="${TEMPLATE_GRUB_CONFIG/\$\{version_grub\}/${version_grub}}"
        [ -f "$template_grub_config" ] ||
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "no such file: $template_grub_config"
        template_morse_config="${TEMPLATE_GRUB_CONFIG_MORSE/\$\{version_grub\}/${version_grub}}"
        [ -f "$template_morse_config" ] ||
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "no such file: $template_morse_config"
        file_modules_install="${TEMPLATE_GRUB_MODULES_INSTALL/\$\{version_grub\}/${version_grub}}"
        [ -f "$file_modules_install" ] ||
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "no such file: $file_modules_install"
        source "./$file_modules_install"
        ;;
      '--payload-dir')
        [ "$arg__payload_dir" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--payload-dir: duplicate option"
        [ "${2:0:2}" = '--' ] || [ "${2}" = '' ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--payload-dir: missing argument"
        opt=$2
        n+=1
        arg__payload_dir="${opt%/}"
        [ -d "${arg__payload_dir}" ] || {
          mkdir -p "${arg__payload_dir}" ||
            go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--payload-dir: cannot create folder"
        }
        pushd "${arg__payload_dir}" &>/dev/null ||
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--payload_dir: no such directory: $arg__payload_dir"
        arg__payload_dir=$(dirs -l +0)
        popd &>/dev/null
        opt__payload_dir=1
        ;;
      '--config-default')
        [ "$arg__config_default" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-default: duplicate option"
        [ "${2:0:2}" = '--' ] || [ "$2" = '' ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-default: missing argument"
        opt=$2
        n+=1
        case "$opt" in
          [0-9]|[0-9][0-9])
            arg__config_default=${opt}
            ;;
          *)
            go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-default: parameter mismatch: $opt"
            ;;
        esac
        ;;
      '--config-keymap')
        [ "$arg__config_keymap" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-keymap: duplicate option"
        [ "${2:0:2}" = '--' ] || [ "$2" = '' ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-keymap: missing argument"
        opt=${2,,}
        n+=1
        arg__config_keymap=${opt}
        ;;
      '--config-background')
        [ ${#grub_background[@]} -lt ${#BACKGROUND_CBFS[@]} ] ||
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-background: array exceeded"
        [ "${2:0:2}" = '--' ] || [ "$2" = '' ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-background: missing argument"
        [ -f "$2" ] ||
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-background: no such file: $2"
        opt=$2
        n+=1
        case "${opt##*.}" in
          'png'|'PNG'|'jpg'|'JPG'|'jpeg'|'JPEG')
            pushd "${opt%/*}" &>/dev/null ||
              go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-background: no such directory: ${opt%/*}"
            grub_background[${#grub_background[@]}]=$(dirs -l +0)"/${opt##*/}"
            popd &>/dev/null
            ;;
          *)
            go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-background: extension not supported: ${opt##*.}"
            ;;
        esac
        opt__config_background=1
        ;;
      '--config-authorization')
        [ "$opt__config_authorization" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-authorization: duplicate option"
        opt__config_authorization=1
        [ "${2:0:2}" = '--' ] || [ "$2" = '' ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-authorization: missing argument"
        opt=${2}
        n+=1
        arg__user0=${opt}
        [ "${3:0:2}" = '--' ] || [ "$3" = '' ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-authorization: missing argument"
        opt=${3}
        n+=1
        arg__user0_pwd=${opt}

        opt='default'
        [ "${4:0:2}" = '--' ] || [ "$4" = '' ] || {
          opt=${4}
          n+=1
          case "${opt,,}" in
            ('default'|'basic'|'advanced')
              ;;
            (*)
              go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-authorization: unknown scheme: $opt"
              ;;
          esac
        }
        arg__auth_scheme=${opt,,}
        ;;
      '--config-tint')
        [ "$opt__config_tint" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-tint: duplicate option"
        opt__config_tint=1
        ;;
      '--config-memtest86+')
        [ "$opt__config_memtest86" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-memtest86+: duplicate option"
        opt__config_memtest86=1
        ;;
      '--config-seabios')
        [ "$opt__config_seabios" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-seabios: duplicate option"
        opt__config_seabios=1
        ;;
      '--config-coreinfo')
        [ "$opt__config_coreinfo" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-coreinfo: duplicate option"
        opt__config_coreinfo=1
        ;;
      '--config-nvramcui')
        [ "$opt__config_nvramcui" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-nvramcui: duplicate option"
        opt__config_nvramcui=1
        ;;
      '--config-no-poweroff')
        [ "$opt__config_no_poweroff" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-no-poweroff: duplicate option"
        opt__config_no_poweroff=1
        ;;
      '--config-morse')
        [ "$opt__config_morse" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-morse: duplicate option"
        opt__config_morse=1
        ;;
      '--config-antifreeze')
        [ "$opt__config_antifreeze" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-antifreeze: duplicate option"
        opt__config_antifreeze=1
        ;;
      '--config-timeout')
        [ "$opt__config_timeout" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-timeout: duplicate option"
        opt__config_timeout=1
        ;;
      # jobs
      '--help')
        ;&
      '--usage')
        opt__usage=1
        ;;
      '--clean')
        opt__clean=1
        ;;
      '--mostlyclean')
        opt__mostlyclean=1
        ;;
      '--remove')
        opt__remove=1
        ;;
      '--gen-keymaps')
        opt__gen_keymaps=1
        ;;
      '--show-snippets')
        opt__show_snippets=1
        ;;
      '--gen-payload')
        opt__gen_payload=1
        ;;
      '--gen-payload-extended')
        opt__gen_payload_extended=1
        ;;
      '--gen-config')
        opt__gen_config=1
        ;;
      '--strip-comments')
        [ "$opt__strip_comments" ] &&
          go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--strip-comments: duplicate option"
        opt__strip_comments=1
        ;;
      *)
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "unknown option: ${opt}"
        ;;
    esac
    shift $n
  done
  if [ $# -ne 0 ]; then
    _warn "... $# parameters were skipped."
  else
    _pass "... all parameters parsed."
  fi

  _funcstop "$FUNCNAME"
}

check_settings()
{
  _funcstart "$FUNCNAME"

  _info "Check settings ..."

  # Output Settings
  _dbug "${FG_MAGENTA}Output Settings ...${FG_DEFAULT}"
  # --debug
  [ "$DEBUG" -eq 0 ] ||
    _mesg "Debug output is activated."
  # --mute
  [ "$opt__mute" ] &&
    _mesg "Standard output of external processes is muted."

  # --toolbox
  [ "$arg__toolbox" ] && {
    _dbug "${FG_MAGENTA}Toolbox, Projects, Versions ...${FG_DEFAULT}"
    _mesg "Toolbox Folder:${RTAB}${arg__toolbox}"
    _mesg "Path to GRUB:${RTAB}${SET_BOLD}${path_to_grub}${CLR_INTENSITY}"
    _mesg "grub-mklayout:${RTAB}${bin__grub_mklayout:?is not set}"
    _mesg "grub-mkstandalone:${RTAB}${bin__grub_mkstandalone}"
    _dbug "GRUB Version:${RTAB}${version_grub:?is not set}"
  }

  # --payload-dir
  [ "$opt__payload_dir" ] && {
    [ "${arg__payload_dir:?is not set}" ] && {
      _dbug "${FG_MAGENTA}Payload Folder ...${FG_DEFAULT}"
      _mesg "Payload Folder:${RTAB}${arg__payload_dir}"
    }
  }

  # --gen-config
  [ "$opt__gen_config" ] && {
    _dbug "${FG_MAGENTA}GRUB Configuration ...${FG_DEFAULT}"

    _mesg "Configuration Template:${RTAB}$template_grub_config"
    [ -f "$template_grub_config" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "no such file: $template_grub_config"

    _mesg "Configuration Changes:"
    _mesg "${LIITEM}Keymap Snippet:${RTAB}$SNIPPET_COMMENTS"
    [ "$opt__gen_keymaps" ] || [ -f "${arg__payload_dir}/$SNIPPET_COMMENTS" ] ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "no such file: ${arg__payload_dir}/$SNIPPET_COMMENTS"

    # --config-timeout
    [ "$opt__config_timeout" ] ||
      _mesg "${LIITEM}Timeout:${RTAB}none"

    # --config-default
    [ "$arg__config_default" ] &&
      _mesg "${LIITEM}Default Entry:${RTAB}${arg__config_default}"

    # --config-keymap
    [ "$arg__config_keymap" ] &&
      _mesg "${LIITEM}Keymap:${RTAB}${arg__config_keymap}"

    # --config-no-poweroff
    [ "$opt__config_no_poweroff" ] &&
      _mesg "${LIITEM}Entry “Power off”:${RTAB}configured to be removed"

    # --config-seabios
    [ "$opt__config_seabios" ] ||
      _mesg "${LIITEM}Entry “SeaBIOS”:${RTAB}configured to be removed"

    # --config-coreinfo
    [ "$opt__config_coreinfo" ] ||
      _mesg "${LIITEM}Entry “Coreinfo”:${RTAB}configured to be removed"

    # --config-nvramcui
    [ "$opt__config_nvramcui" ] ||
      _mesg "${LIITEM}Entry “NVRAMCUI”:${RTAB}configured to be removed"

    # --config-antifreeze
    [ "$opt__config_antifreeze" ] ||
      _mesg "${LIITEM}Antifreeze Feature:${RTAB}configured to be removed"

    # --config-authorization
    [ "$opt__config_authorization" ] && {
      _mesg "${LIITEM}Authorization:${RTAB}$arg__auth_scheme"
    }

    # --config-morse
    if [ "$opt__config_morse" ]; then
      _mesg "Morse Feedback:"
      _warn "${LIITEM}GRUB2’s access to audio is subject to failure, please check."
    else
      _mesg "${LIITEM}Entry “Morse”:${RTAB}configured to be removed"
    fi
  }

  # --strip-comments

  # --show_snippets

  # --gen-payload-extended

  # --gen-payload

  # --config-background
  _dbug "${FG_MAGENTA}GRUB Background Links ...${FG_DEFAULT}"
  _mesg "GRUB Backgrounds:"
  declare i
  for i in "${!grub_background[@]}"; do
    _mesg "${LIITEM}Background $i:${RTAB}${grub_background[$i]}"
  done
  unset i

  _pass "... settings checked."

  _funcstop "$FUNCNAME"
}

parse_debug()
{
  declare i=

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

  true
}

gen_config()
{
  _funcstart "$FUNCNAME"

  declare -r new_config="${arg__payload_dir}/${template_grub_config##*\/}"
  declare -r snippet_keymap="${arg__payload_dir}/$SNIPPET_COMMENTS"

  _info "Generate GRUB Configuration File ..."

  _mesg "Copy template file and update list of keymaps ..."
  sed -r \
    -e '/^# Keymap/r '"$snippet_keymap"'' \
    -e '/^# Keymap/,/^keymap/d;' \
    "$template_grub_config" \
    > "$new_config" ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"

  [ "$opt__config_seabios" ] || {
    _mesg "Remove menu entry “SeaBIOS” ..."
    sed -r -i \
      -e '/^function func_menu_seabios \{/,/^\}$/d;' \
      -e '/# Hotkeys in use\:/s/, b//;' \
      -e '/^\tfunc_menu_seabios$/d;' \
      "$new_config" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  }

  [ "$opt__config_tint" ] || {
    _mesg "Remove menu entry “Tint Game” ..."
    sed -r -i \
      -e '/^function func_menu_tint \{/,/^\}$/d;' \
      -e '/^\tfunc_menu_tint$/d;' \
      "$new_config" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  }

  [ "$opt__config_memtest86" ] || {
    _mesg "Remove menu entry “Memtest86+” ..."
    sed -r -i \
      -e '/^function func_menu_memtest86 \{/,/^\}$/d;' \
      -e '/^\tfunc_menu_memtest86$/d;' \
      "$new_config" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  }

  [ "$opt__config_coreinfo" ] || {
    _mesg "Remove menu entry “Coreinfo” ..."
    sed -r -i \
      -e '/^function func_menu_coreinfo \{/,/^\}$/d;' \
      -e '/# Hotkeys in use\:/s/, f//;' \
      -e '/^\tfunc_menu_coreinfo$/d;' \
      "$new_config" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  }

  [ "$opt__config_antifreeze" ] || {
    _mesg "Remove Antifreeze Feature ..."
    sed -r -i \
      -e '/^function func_menu_search \{/,/^\}$/s/intel_idle.max_cstate=. //;' \
      "$new_config" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  }

  [ "$opt__config_nvramcui" ] || {
    _mesg "Remove menu entry “NVRAMCUI” ..."
    sed -r -i \
      -e '/^function func_menu_nvramcui \{/,/^\}$/d;' \
      -e '/# Hotkeys in use\:/s/, n//;' \
      -e '/^\tfunc_menu_nvramcui$/d;' \
      "$new_config" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  }

  [ "$opt__config_no_poweroff" ] && {
    _mesg "Remove menu entry “Poweroff” ..."
    sed -r -i \
      -e '/^function func_menu_poweroff \{/,/^\}$/d;' \
      -e '/# Hotkeys in use\:/s/, h//;' \
      -e '/^\tfunc_menu_poweroff$/d;' \
      "$new_config" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  }

  [ "$opt__config_morse" ] || {
    _mesg "Remove Morse Feedback Feature ..."
    sed -r -i \
      -e '/^function func_menu_morse \{/,/^\}$/d;' \
      -e '/if \[ \"\$fmm_enable_morse\".* \];/,/fi.*end morse feedback.*/d;' \
      -e '/# Hotkeys in use\:/s/, m//;' \
      -e '/^\tfunc_menu_morse$/d;' \
      "$new_config" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  }

  [ "$opt__config_timeout" ] || {
    _mesg "Apply ‘set timeout=-1’ ..."
    sed -r -i \
      -e '/set timeout=/c\\tset timeout=-1' \
      "$new_config" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  }

  [ "$arg__config_keymap" ] && {
    _mesg "Apply keymap ‘$arg__config_keymap’ ..."
    [ "$(sed -r \
      -e '/# Keymap/,/^[ \t]*keymap/!d;' \
      -e '/# Keymap/d; /^[ \t]*keymap/d;' \
      -e "/ $arg__config_keymap[ $]/!d;" \
      "$new_config")" ] ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "No such keymap: ${arg__config_keymap}"
    sed -r -i \
      -e "/^function func_menu \{/,/^\}/s/(^[ \t]*keymap[ \t]*)(.*)/\1${arg__config_keymap}/;" \
      "${new_config}" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  }

  [ "$opt__config_authorization" ] && {
    declare user0="$arg__user0"
    declare user0_pwd="$arg__user0_pwd"
    declare superusers="$user0"
    declare auth_scheme="$arg__auth_scheme"
    declare type_pwd='password'

    _warn "Activate GRUB authorization scheme “$auth_scheme” ..."
    case "$auth_scheme" in
      ('advanced')
        sed -r -i \
          -e 's/(--hotkey=.?[^hx].?)([ \t]*--unrestricted[ \t]*)(\{.*)/\1 \3/;' \
          "$new_config" ||
            go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
        ;&
      ('basic')
        ;;
      ('default')
        sed -r -i \
          -e 's/(--hotkey=.?[^q].?)([ \t]+)(\{.*)/\1 --unrestricted \3/;' \
          "$new_config" ||
            go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
        ;;
      (*)
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "unknown scheme of authorization"
        ;;
    esac

    # determine password type
    [ "$(echo "$user0_pwd" | sed -r -e '/grub.pbkdf2/!d;' -)" ] &&
      type_pwd='password_pbkdf2'

    # feedback
    _mesg "${LIITEM}user0:${RTAB}$user0"
    _mesg "${LIITEM}user0 $type_pwd:${RTAB}$user0_pwd"
    _mesg "${LIITEM}superusers:${RTAB}$superusers"
    sed -r -i \
      -e "/#password user0/s/(^[ \t]*)(#password.*$)/\1$type_pwd $user0 \'$user0_pwd\'/;" \
      -e "/#set superusers=/s/(^[ \t]*)(#set superusers=.*$)/\1set superusers=\'$superusers\'/;" \
      "$new_config" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"

    _mesg "... authorization activated, note listed credentials!"
  }

  [ "$arg__config_default" ] && {
    _mesg "Apply new default menu entry $arg__config_default ..."

    declare space='<%%space%%>'
    declare -a entries

    entries=($(cat "$new_config" | sed -r -e '/^[ \t]*menuentry/!d;{;s/(^[ \t]*menuentry )('.*')( --hotkey.*)/\2/;};' -e "s/[ \t]/$space/g;" -))
    [ $arg__config_default -ge ${#entries[@]} ] &&
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "--config-default: number of menu entries (0..$((${#entries[@]}-1))) exceeded"
    _mesg "${INDENT}$(echo ${entries[$arg__config_default]} | sed -r -e 's/'"$space"'/ /g;')"

    sed -r -i \
      -e "s/(^[ \t]*set default=)(0)/\1$arg__config_default/;" \
      "$new_config" ||
        go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"

    unset entries
    unset space
  }

  _pass "... file generated:${RTAB}$new_config"

  [ "$opt__config_morse" ] && {
    _info "Generate GRUB Morse Feedback configuration file ..."
    cp -f "$template_morse_config" "${arg__payload_dir}/${template_morse_config##*\/}" ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"

    _pass "... file generated:${RTAB}${arg__payload_dir}/${template_morse_config##*\/}"
  }

  _funcstop "$FUNCNAME"
}

# $1: File to strip in place
strip_comments()
{
  _funcstart "$FUNCNAME"

  _info "Strip comments from $1 ..."
  # with each comment block, remove appended empty line as well
  sed -r -i \
    -e '/^$/d;' \
    -e '/^[ \t]*#.*/d;' \
    -e '/password/!{;s/[ \t]+#[ \t]+.*//g;};' \
    "$1" ||
      go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  _pass "... comments stripped."

  _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__mkdir" ] && {
    _mesg "${LIITEM}create payload folder"
    retval=0
  }
  [ "$opt__mostlyclean" ] && {
    _mesg "${LIITEM}clean configuration files and symbolic links to background files"
    retval=0
  }
  [ "$opt__clean" ] && {
    _mesg "${LIITEM}clean all generated files"
    retval=0
  }
  [ "$opt__remove" ] && {
    _mesg "${LIITEM}remove empty payload folder"
    retval=0
  }
  [ "$opt__config_background" ] && {
    _mesg "${LIITEM}create symbolic links to specified background images"
    retval=0
  }
  [ "$opt__gen_keymaps" ] && {
    _mesg "${LIITEM}generate keymaps for the GRUB console, as well as corresponding snippet files:"
    _mesg "${INDENT}${LIITEM}snippet file, listing all generated keymap files:"
    _mesg "${INDENT}${INDENT}${INDENT}${SNIPPET_MAPS}"
    _mesg "${INDENT}${LIITEM}snippet file, to be used with invocation of grub-mkstandalone:"
    _mesg "${INDENT}${INDENT}${INDENT}${SNIPPET_GRAFTS}"
    _mesg "${INDENT}${LIITEM}configuration snippet:"
    _mesg "${INDENT}${INDENT}${INDENT}${SNIPPET_COMMENTS}"
    retval=0
  }
  [ "$opt__gen_payload" ] && {
    _mesg "${LIITEM}generate the *.elf payload file for platform i386-coreboot"
    retval=0
  }
  [ "$opt__gen_payload_extended" ] && {
    _mesg "${LIITEM}generate the *.elf payload file for platform i386-coreboot, extended version"
    retval=0
  }
  [ "$opt__gen_config" ] && {
    _mesg "${LIITEM}generate configuration files"
    retval=0
  }
  [ "$opt__strip_comments" ] && {
    _mesg "${LIITEM}strip comments from configuration files"
    retval=0
  }
  [ "$opt__show_snippets" ] && {
    _mesg "${LIITEM}show the content of generated snippet files"
    retval=0
  }
  [ "$retval" -eq 0 ] ||
    _mesg "${INDENT}none"

  _funcstop "$FUNCNAME"
  return "$retval"
}

enter_payload_folder() {
  _funcstart "$FUNCNAME"

  _info "Enter payload folder ‘$arg__payload_dir’ ..."
  pushd "${arg__payload_dir}" &>/dev/null ||
    go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  _pass "... payload folder entered."

  _funcstop "$FUNCNAME"
}

leave_payload_folder() {
  _funcstart "$FUNCNAME"

  _info "Return to toolchain folder ..."
  popd &>/dev/null ||
    go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO"
  _pass "... successfully returned."

  _funcstop "$FUNCNAME"
}

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[3]}"
  declare osid=
  declare -r -a commands_script=(
    'source'
    'echo'
    'printf'
    'cat'
    'sed'
    'rm'
    'mv'
    'cd'
    'sleep'
    'exit'
  )
  declare -r -a commands__gen_keymaps=(
    'ckbcomp'
  )
  declare -r PATH_TO_WD="$(pwd)"
  declare -r KEYMAP_DEFAULT="us"
  declare -r SNIPPET_MAPS="keymap-maps.txt"
  declare -r SNIPPET_GRAFTS="keymap-grafts.txt"
  declare -r SNIPPET_COMMENTS="keymap-comments.txt"
  declare -r TEMPLATE_GRUB_MODULES_INSTALL="../templates/grub@\${version_grub}_modules-install.conf"

  declare opt__mute=
  declare opt__usage=
  declare opt__clean=
  declare opt__mostlyclean=
  declare opt__remove=
  declare opt__gen_keymaps=
  declare opt__show_snippets=
  declare opt__gen_payload=
  declare opt__gen_payload_extended=
  declare opt__gen_config=
  declare opt__config_tint=
  declare opt__config_memtest86=
  declare opt__config_morse=
  declare opt__config_background=
  declare opt__config_authorization=
  declare opt__config_timeout=
  declare opt__config_seabios=
  declare opt__config_nvramcui=
  declare opt__config_coreinfo=
  declare opt__config_no_poweroff=
  declare opt__config_antifreeze=
  declare opt__strip_comments=
  declare opt__payload_dir=

  declare arg__toolbox=
  declare arg__payload_dir=
  declare arg__user0=
  declare arg__user0_pwd=
  declare arg__auth_scheme='default'
  declare arg__config_default=
  declare arg__config_keymap=

  declare path_to_grub=               # directly related to $arg__toolbox
  declare version_grub=
  declare bin__grub_mklayout=
  declare bin__grub_mkstandalone=
  declare file_elf_extended=
  declare file_elf=
  declare file_memdisk_config=
  declare file_modules_minimal=
  declare file_modules_preload=
  declare template_grub_config=
  declare template_morse_config=

  declare snippet_grafts=
  declare -a snippet_maps=('' '' '' '')
  declare -a snippet_comments=('' '' '' '')
  declare modules_install=

  declare -a grub_background

  _head "Zerocat Coreboot Machines ${PROJECT_NUMBER}”"
  _head "$scriptname – Generate GRUB 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__mostlyclean" ] && {
          enter_payload_folder
          clean \
            *.cfg \
            "${GRUB_BG/\$\{ibg\}/?}.*"
          leave_payload_folder
        }
        [ "$opt__clean" ] && {
          enter_payload_folder
          clean \
            *.cfg \
            "${GRUB_BG/\$\{ibg\}/?}.*" \
            $SNIPPET_MAPS \
            $SNIPPET_GRAFTS \
            $SNIPPET_COMMENTS \
            *.gkb \
            *.elf
          leave_payload_folder
        }
        [ "$opt__remove" ] && {
          remove
        }
        [ "$opt__config_background" ] && {
          enter_payload_folder
          _info "Create symbolic links to background images ..."
          declare grub_bg
          declare i
          for i in "${!grub_background[@]}"; do
            ext="${grub_background[$i]}"
            ext="${ext##*.}"
            ext="${ext,,}"
            ext="${ext/e/}"
            grub_bg="${GRUB_BG/\$\{ibg\}/$i}.$ext"
            ln -f -s "${grub_background[$i]}" "$grub_bg" ||
              go_exit "$BASH_SOURCE" "$FUNCNAME" "$LINENO" "failed to create symbolic link"
            _mesg "Background ${i}:${RTAB}${SET_BOLD}$(ls -l $grub_bg | sed -r -e 's/(^.* )(.* -> .*)/\2/;' -)${CLR_INTENSITY}"
          done
          unset i
          unset grub_bg
          _pass "... symbolic links created."
          leave_payload_folder
        }
        [ "$opt__gen_keymaps" ] && {
          enter_payload_folder
          gen_keymaps
          display_snippet "$SNIPPET_COMMENTS"
          leave_payload_folder
        }
        [ "$opt__gen_payload" ] && {
          gen_payload
        }
        [ "$opt__gen_payload_extended" ] && {
          gen_payload_extended
        }
        [ "$opt__gen_config" ] && {
          gen_config
        }
        [ "$opt__strip_comments" ] && {
          enter_payload_folder
          [ -f "${template_grub_config##*\/}" ] && {
            strip_comments "${template_grub_config##*\/}"
          }
          [ -f "${template_morse_config##*\/}" ] && {
            strip_comments "${template_morse_config##*\/}"
          }
          leave_payload_folder
        }
        [ "$opt__show_snippets" ] && {
          enter_payload_folder
          display_snippet "$SNIPPET_MAPS"
          display_snippet "$SNIPPET_GRAFTS"
          display_snippet "$SNIPPET_COMMENTS"
          leave_payload_folder
        }
      }
    }
  }
  _info "Good Bye."

  _funcstop "$FUNCNAME"

  trap - ERR SIGINT
}

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