#!/usr/bin/env bash


###
#
# Options Section
#
###

set -e
set -u
set -o pipefail


###
#
# Variable Section
#
###

REPOSITORY_URL='https://repository.privlab.it/archive/raspberrypi'
REPOSITORY_PATH='/var/lib/raspi-templates'


###
#
# Function Section
#
###

function configuration () {
  local CONFIGURATION_REALPATH="${1}"
  local CONFIGURATION_BASENAME
  CONFIGURATION_BASENAME=$(/usr/bin/basename "${CONFIGURATION_REALPATH}")
  case "${CONFIGURATION_BASENAME}" in
    00-esp.conf)
      /usr/bin/tee "${CONFIGURATION_REALPATH}" &> '/dev/null' <<EOF
[Partition]
Type=esp
Label=FIRMWARE
SizeMinBytes=256M
SizeMaxBytes=256M
Format=vfat
CopyFiles=/boot/firmware/:/
EOF
    ;;
    25-root.conf)
      /usr/bin/tee "${CONFIGURATION_REALPATH}" &> '/dev/null' <<EOF
[Partition]
Type=root
Label=Debian GNU/Linux
SizeMinBytes=512M
SizeMaxBytes=32768M
Format=ext4
CopyFiles=/:/
CopyFiles=/boot:/boot
ExcludeFilesTarget=/boot/firmware/
EOF
    ;;
    cleanup)
      /usr/bin/tee "${CONFIGURATION_REALPATH}" &> '/dev/null' <<EOF
## Cleanup Path Specification
##    Absolute path to the files or folders
##    which will be deleted from the template.
##
/dev/*
/lost+found/*
/media/*
/mnt/*
/proc/*
/run/*
/sys/*
/tmp/*
/tmp/.??*
/var/tmp/*
EOF
    ;;
    image.conf)
      /usr/bin/tee "${CONFIGURATION_REALPATH}" &> '/dev/null' <<EOF
######################################
##                                  ##
## Raspberry Pi Image Configuration ##
##                                  ##
######################################


##
## System Settings
##


## Hostname
##    Set the hostname of the Raspberry Pi.
##
HOSTNAME=''

## Root Password
##    Set the password to access the Raspberry Pi.
##
ROOT_PASSWORD=''

## Secure Shell Public Key
##    Set the public SSH key to access the
##    Raspberry Pi through an SSH connection.
##
SSH_PUBKEY=''


##
## Wi-Fi Settings
##


## Wi-Fi SSID
##    Set the Wi-Finetwork name.
##
WIFI_SSID=''

## Wi-Fi Password
##    Set the Wi-Fi network password.
##
WIFI_PASSWORD=''

## Wi-Fi Country
##    Set the Wi-Fi regulatory identifier.
##
WIFI_COUNTRY='00'

## Wi-Fi Key Management
##    Set the accepted authenticated key management protocol.
##
##    Value                           Description
##    ----------------------------------------------------------
##    WPA-PSK                         Wi-Fi Protected Access 2
##    SAE                             Wi-Fi Protected Access 3
##
WIFI_KEY_MGMT='WPA-PSK'

## WiFi Scan SSID
##    Set the status of scanning with specified probe request frames.
##
##    Value                           Description
##    ----------------------------------------------------------
##    0                               Disabled
##    1                               Enabled
##
WIFI_SCAN_SSID='0'


##
## Certificate Settings
##


## Certificate
##    Indicate whether certificates are generated.
##
##    ----------------------------------------------------------
##    true                            Generate Certificates
##    false                           Skip Function
##
CERTIFICATE='false'

## RSA Keysize
##    Set the keysize for the RSA-Key.
##
##    Value                           Description
##    ----------------------------------------------------------
##    2048                            2048 Bit
##    3072                            3072 Bit
##    4096                            4096 Bit
##    ....                            .... Bit
##
RSA_KEYSIZE='3072'

## ECDSA Keysize
##    Set the keysize for the ECDSA-Key.
##
##    Value                           Description
##    ----------------------------------------------------------
##    secp384r1                       384 Bit
##    secp521r1                       521 Bit
##    .........                       ... Bit
##
ECDSA_KEYSIZE='secp384r1'

## Certificate Subject
##    Set the certificates subject.
##
##      Country               -   Country Name (2 letter code)
##      State                 -   State or Province Name (full name)
##      Locality              -   Locality Name (e.g. city)
##      Organization          -   Organization Name (e.g. company)
##      Organizational Unit   -   Organizational Unit Name (e.g. section)
##      Common Name           -   Common Name (e.g. server FQDN or YOUR name)
##
COUNTRY='GB'
STATE='Cambridgeshire'
LOCALITY='Cambridge'
ORGANIZATION='Raspberry Pi Foundation'
ORGANIZATIONAL_UNIT=''
COMMON_NAME='Raspberry Pi OS'


##
## Diffie-Hellman Settings
##


## Diffie-Hellman Parameters
##    Indicate whether Diffie-Hellman parameters are generated.
##    NOTE: THIS FUNCTION INCREASE THE PREPARATION TIME!
##
##    ----------------------------------------------------------
##    true                            Generate DHPEM
##    false                           Skip Function
##
DHPEM='false'

## Diffie-Hellman Parameters Keysize
##    Set the keysize for the Diffie-Hellman Parameters.
##    Each keysize generate a separate Diffie-Hellman parameter.
##      e.g. "512 1024 2048 4096"
##
DHPEM_SIZE="512 1024 2048 4096"
EOF
    ;;
    .version)
      local DATE
      DATE=$(/usr/bin/date +%s)
      /usr/bin/tee "${CONFIGURATION_REALPATH}" &> '/dev/null' <<EOF
${DATE}
EOF
    ;;
  esac
}

function error () {
  if [[ -n "${1:-}" ]]; then
    /usr/bin/echo -n -e "${1}"
  fi
  if [[ -n "${2:-}" ]]; then
    /usr/bin/echo -n -e "${2}"
  fi
  exit 1
}

function consistency () {
  local INPUT="${1}"
  output 'Check the input for consistency ...'
  if [[ -f "${INPUT}" ]]; then
    /usr/sbin/fdisk --list "${INPUT}" 2> '/dev/null' | \
    /usr/bin/grep --quiet 'Disklabel type: gpt' || \
      error '\r\rCheck the input for consistency [ERROR]\n' \
            "  => The disklabel type 'gpt' could not be determined.\n"
    /usr/sbin/fdisk --list "${INPUT}" 2> '/dev/null' | \
    /usr/bin/grep --quiet 'EFI System' || \
      error '\r\rCheck the input for consistency [ERROR]\n' \
            "  => The partition table 'EFI System' could not be determined.\n"
    /usr/sbin/fdisk --list "${INPUT}" 2> '/dev/null' | \
    /usr/bin/grep --quiet 'Linux root (ARM-64)' || \
      error '\r\rCheck the input for consistency [ERROR]\n' \
            "  => The partition table 'Linux root (ARM-64)' could not be determined.\n"
    CONSISTENCY_TYPE='IMAGE'
  elif [[ -d "${INPUT}" ]]; then
    consistency_chroot "${INPUT}" &> '/dev/null' || \
      error '\r\rCheck the input for consistency [ERROR]\n' \
            '  => The root environment could not be determined.\n'
    CONSISTENCY_TYPE='TEMPLATE'
  elif [[ -b "${INPUT}" ]]; then
    /usr/sbin/fdisk --list "${INPUT}" 2> '/dev/null' | \
    /usr/bin/grep --quiet 'Disklabel type: gpt' || \
      error '\r\rCheck the input for consistency [ERROR]\n' \
            "  => The disklabel type 'gpt' could not be determined.\n"
    /usr/sbin/fdisk --list "${INPUT}" 2> '/dev/null' | \
    /usr/bin/grep --quiet 'EFI System' || \
      error '\r\rCheck the input for consistency [ERROR]\n' \
            "  => The partition table 'EFI System' could not be determined.\n"
    /usr/sbin/fdisk --list "${INPUT}" 2> '/dev/null' | \
    /usr/bin/grep --quiet 'Linux root (ARM-64)' || \
      error '\r\rCheck the input for consistency [ERROR]\n' \
            "  => The partition table 'Linux root (ARM-64)' could not be determined.\n"
    CONSISTENCY_TYPE='STORAGE'
  else
    error '\r\rCheck the input for consistency [ERROR]\n' \
          '  => The input could not be determined as a (image|storage|template).\n'
  fi
  output '\r\rCheck the input for consistency [OK]\n'
}

function consistency_chroot () {
  local CHROOT_PATH="${1}"
  /usr/sbin/chroot "${CHROOT_PATH}" /bin/sh -c 'exit 0'
}

function initramfs_chroot () {
  local CHROOT_PATH="${1}"
  /usr/sbin/chroot "/mnt/${LOOP_DEVICE_BASENAME}" /bin/sh -c "/usr/sbin/update-initramfs -k 'all' -c" || \
    error "\r\rCreate initial ram file system [ERROR]\n" \
          "  => The initial ram file system could not be created.\n"
}

function help () {
  local HELP="${1}"
  case "${HELP}" in
    archive)
      read -r -d '' HELP << EOL
Usage: raspi-image -a (Template)

Description:
  Archives a Raspberry Pi template from the local repository,
  computes its checksum, and saves it to the current directory.

  Templates available in the local repository can be listed
  with the command 'raspi-image -i'.
EOL
    ;;
    chroot)
      read -r -d '' HELP << EOL
Usage: raspi-image -c (Image / Storage / Template)

Description:
  Opens a CHROOT environment for a Raspberry Pi template, storage, or image.
  The following host filesystems are binded and slaved into the CHROOT environment:
    - 'devtmpfs' : '/dev'
    - 'mqueue'   : '/dev/mqueue'
    - 'devpts'   : '/dev/pts'
    - 'proc'     : '/proc'
    - 'sys'      : '/sys'
    - 'tmpfs'    : '/run'
  All mounted filesystems are cleanly unmounted when exiting the CHROOT environment.
EOL
    ;;
    download)
      read -r -d '' HELP << EOL
Usage: raspi-image -d (Template)

Description:
  Downloads a Raspberry Pi template from the online repository or updates it.
  If the local template is a newer version than the online repository,
  user interaction to delete the local template is required before overwriting.
  If the version in the online repository is newer, the local template is replaced
  by the version from the online repository.
EOL
    ;;
    generate)
      read -r -d '' HELP << EOL
Usage: raspi-image -g (Image)

Description:
  Creates a Raspberry Pi image from a template.
  It first checks whether the required template is available in the local repository;
  if not, it attempts to retrieve it from the online repository.
  An editor will opens to configure the image. Available customization options:
    - 'Hostname'
    - 'Authentication'
    - 'Wi-Fi settings'
    - 'Certificate issuance'
    - 'DHPEM generation'
EOL
    ;;
    help)
      read -r -d '' HELP << EOL
Usage: raspi-image (Option) (Image / Storage / Template)

Options:
  [ -a ] [ Template ]                   |  Archive a Raspberry Pi template
  [ -c ] [ Image / Storage / Template]  |  Open a chroot environment in a Raspberry Pi image, storage or template
  [ -d ] [ Template ]                   |  Dowload / Update a Raspberry Pi template to local repository
  [ -g ] [ Image ]                      |  Generate a Raspberry Pi image
  [ -h ]                                |  Display help
  [ -i ]                                |  Display repository information
  [ -m ] [ Image / Storage ]            |  Mount / Umount a Raspberry Pi image or storage
  [ -u ] [ Template ]                   |  Update an prepare Raspberry Pi template to a new version
EOL
    ;;
    info)
      read -r -d '' HELP << EOL
Usage: raspi-image -i

Description:
  Lists all available Raspberry Pi templates from the local and
  online repositories and compares their versions.
EOL
    ;;
    mount)
      read -r -d '' HELP << EOL
Usage: raspi-image -m (Image / Storage)

Description:
  Mount at path '/mnt' either a Raspberry Pi image or a storage filesystem;
  if the Raspberry Pi image or storage filesystem is already mounted, unmount it.
EOL
    ;;
    update)
      read -r -d '' HELP << EOL
Usage: raspi-image -u (Template)

Description:
  An editor opens displaying a list of files and folders to be removed from the
  Raspberry Pi template. After removal, the template version is updated accordingly.
  Since this Raspberry Pi template is, in a broad sense, a fork, it should be
  considered whether renaming it would be appropriate.
EOL
    ;;
  esac
  /usr/bin/echo -e "${HELP}"
}

function image_download () {
  local TEMPLATE="${1}"
  local RESTORE_DIRECTORY
  RESTORE_DIRECTORY=$(/usr/bin/pwd)
  if [[ -z "${TEMPLATE_ONLINE_VERSION}" ]]; then
    local TEMPLATE_ONLINE_VERSION
    TEMPLATE_ONLINE_VERSION=$(
      /usr/bin/wget --quiet --output-document='-' "${REPOSITORY_URL}" 2> '/dev/null' | \
      /usr/bin/tr '\n' ' ' | \
      /usr/bin/sed --expression='s/</\n</g' | \
      /usr/bin/grep --ignore-case --extended-regexp '^<a ' | \
      /usr/bin/sed --quiet --regexp-extended 's/.*href=["'"'"']?([^"'"'"' >]+).*/\1/p' | \
      /usr/bin/sed --expression='s,^\./,,; s/\?.*$//' | \
      /usr/bin/grep --invert-match --extended-regexp '^(\.\./|/)$' | \
      /usr/bin/grep --invert-match '/$' | \
      /usr/bin/sed --expression='s/%20/ /g' | \
      /usr/bin/uniq | \
      /usr/bin/grep --extended-regexp "${TEMPLATE}\.[0-9]+\.tar\.xz$"
    )
  fi
  TEMPLATE_ONLINE_VERSION=$(/usr/bin/awk -F '.' '{print $NF}' <<< "${TEMPLATE_ONLINE_VERSION%.tar.xz}")
  output "Download Raspberry Pi template '${TEMPLATE}' ..."
  /usr/bin/wget --quiet \
                --output-document="${TMP}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz" \
                --no-hsts "${REPOSITORY_URL}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz" &> '/dev/null' || \
    error "\r\rDownload Raspberry Pi template '${TEMPLATE}' [ERROR]\n" \
          "  => The online repository is not reachable.\n"
  /usr/bin/wget --quiet \
                --output-document="${TMP}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz.sha512sum" \
                --no-hsts "${REPOSITORY_URL}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz.sha512sum" &> '/dev/null' || \
    error "\r\rDownload Raspberry Pi template '${TEMPLATE}' [ERROR]\n" \
          "  => The online repository is not reachable.\n"
  output "\r\rDownload Raspberry Pi template '${TEMPLATE}' [OK]\n"
  output "Check the consistency of '${TEMPLATE}' ..."
  cd "${TMP}"
  /usr/bin/sha512sum --check \
                     --strict "${TMP}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz.sha512sum" &> '/dev/null' || \
    error "\r\rCheck the consistency of '${TEMPLATE}' [ERROR]\n" \
          "  => The Raspberry Pi template '${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz' does not match the sha512 checksum.\n"
  cd "${RESTORE_DIRECTORY}"
  output "\r\rCheck the consistency of '${TEMPLATE}' [OK]\n"
  output "Decompress Raspberry Pi template '${TEMPLATE}' ..."
  /usr/bin/install --directory "${REPOSITORY_PATH}/${TEMPLATE}" || \
    error "\r\rDecompress Raspberry Pi template '${TEMPLATE}' [ERROR]\n" \
          "  => The Raspberry Pi template directory '${REPOSITORY_PATH}/${TEMPLATE}' could not be installed.\n"
  /usr/bin/tar --extract \
               --directory="${REPOSITORY_PATH}/${TEMPLATE}" \
               --file="${TMP}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz" || \
    error "\r\rDecompress Raspberry Pi template '${TEMPLATE}' [ERROR]\n" \
          "  => The Raspberry Pi template '${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz' could not be decompressed.\n"
  output "\r\rDecompress Raspberry Pi template '${TEMPLATE}' [OK]\n"
}

function image_fsck () {
  local LOOP_DEVICE_REALPATH="${1}"
  output "Check and repair file system on '${LOOP_DEVICE_REALPATH}p1' ..."
  /usr/sbin/fsck.vfat -a -t -V -w "${LOOP_DEVICE_REALPATH}p1" &> '/dev/null' || \
    error "\r\rCheck and repair file system on '${LOOP_DEVICE_REALPATH}p1' [ERROR]\n" \
          "  => The image file system could not be repaired.\n"
  output "\r\rCheck and repair file system on '${LOOP_DEVICE_REALPATH}p1' [OK]\n"
  output "Check and repair file system on '${LOOP_DEVICE_REALPATH}p2' ..."
  /usr/sbin/fsck.ext4 -p -c -f "${LOOP_DEVICE_REALPATH}p2" &> '/dev/null' || \
    error "\r\rCheck and repair file system on '${LOOP_DEVICE_REALPATH}p2' [ERROR]\n" \
          "  => The image file system could not be repaired.\n"
  output "\r\rCheck and repair file system on '${LOOP_DEVICE_REALPATH}p2' [OK]\n"
}

function loop_device_attach () {
  local INPUT="${1}"
  output 'Attach loop device ...'
  LOOP_DEVICE_REALPATH=$(
    /usr/sbin/losetup --find \
                      --partscan \
                      --show "${INPUT}"
  ) || \
    error '\r\rAttach loop device [ERROR]\n' \
          "  => The loop device is broken.\n"
  output '\r\rAttach loop device [OK]\n'
}

function loop_device_detach () {
  local LOOP_DEVICE_REALPATH="${1}"
  output 'Detach loop device ...'
  /usr/sbin/losetup --detach "${LOOP_DEVICE_REALPATH}" || \
    error '\r\rDetach loop device [ERROR]\n' \
          '  => The loop device could not be detached.\n'
  output '\r\rDetach loop device [OK]\n'
}

function mount_api () {
  local IMAGE_STORAGE_REALPATH="${1}"
  local IMAGE_STORAGE_BASENAME
  IMAGE_STORAGE_BASENAME=$(/usr/bin/basename "${IMAGE_STORAGE_REALPATH}")
  local MOUNTPOINT_PREFIX="/mnt/${IMAGE_STORAGE_BASENAME}"
  output "Mount 'devtmpfs' ..."
  /usr/bin/mount --bind '/dev' \
                 --target "${MOUNTPOINT_PREFIX}/dev" &> '/dev/null' || \
    error "\r\rMount 'devtmpfs' [ERROR]\n" \
          "  => The device 'devtmpfs' could not be mounted in '${MOUNTPOINT_PREFIX}/dev'.\n"
  /usr/bin/mount --make-rslave \
                 --target "${MOUNTPOINT_PREFIX}/dev" &> '/dev/null' || \
    error "\r\rMount 'devtmpfs' [ERROR]\n" \
          "  => The mountpoint '${MOUNTPOINT_PREFIX}/dev' could not be slaved.\n"
  output "\r\rMount 'devtmpfs' [OK]\n"
  output "Mount 'devpts' ..."
  /usr/bin/mount --bind '/dev/pts' \
                 --target "${MOUNTPOINT_PREFIX}/dev/pts" &> '/dev/null' || \
    error "\r\rMount 'devpts' [ERROR]\n" \
          "  => The device 'devpts' could not be mounted in '${MOUNTPOINT_PREFIX}/dev/pts'.\n"
  /usr/bin/mount --make-rslave \
                 --target "${MOUNTPOINT_PREFIX}/dev/pts" &> '/dev/null' || \
    error "\r\rSlave mountpoint 'devpts' [ERROR]\n" \
          "  => The mountpoint '${MOUNTPOINT_PREFIX}/dev/pts' could not be slaved.\n"
  output "\r\rMount 'devpts' [OK]\n"
  output "Mount 'mqueue' ..."
  /usr/bin/mount --bind '/dev/mqueue' \
                 --target "${MOUNTPOINT_PREFIX}/dev/mqueue" &> '/dev/null' || \
    error "\r\rMount 'mqueue' [ERROR]\n" \
          "  => The device 'mqueue' could not be mounted in '${MOUNTPOINT_PREFIX}/dev/mqueue'.\n"
  /usr/bin/mount --make-rslave \
                 --target "${MOUNTPOINT_PREFIX}/dev/mqueue" &> '/dev/null' || \
    error "\r\rMount 'mqueue' [ERROR]\n" \
          "  => The mountpoint '${MOUNTPOINT_PREFIX}/dev/mqueue' could not be slaved.\n"
  output "\r\rMount 'mqueue' [OK]\n"
  output "Mount 'proc' ..."
  /usr/bin/mount --types 'proc' \
                 --source '/proc' \
                 --target "${MOUNTPOINT_PREFIX}/proc" &> '/dev/null' || \
    error "\r\rMount 'proc' [ERROR]\n" \
          "  => The device 'proc' could not be mounted in '${MOUNTPOINT_PREFIX}/proc'.\n"
  output "\r\rMount 'proc' [OK]\n"
  output "Mount 'run' ..."
  /usr/bin/mount --bind '/run' --target "${MOUNTPOINT_PREFIX}/run" || \
    error "\r\rMount 'run' [ERROR]\n" \
          "  => The device 'sysfs' could not be mounted in '${MOUNTPOINT_PREFIX}/run'.\n"
  /usr/bin/mount --make-rslave \
                 --target "${MOUNTPOINT_PREFIX}/run" &> '/dev/null' || \
    error "\r\rSlave mountpoint 'tmpfs' [ERROR]\n" \
          "  => The mountpoint '${MOUNTPOINT_PREFIX}/dev/pts' could not be slaved.\n"
  output "\r\rMount 'run' [OK]\n"
  output "Mount 'sys' ..."
  /usr/bin/mount --types 'sysfs' \
                 --source '/sys' \
                 --target "${MOUNTPOINT_PREFIX}/sys" &> '/dev/null' || \
    error "\r\rMount 'sys' [ERROR]\n" \
          "  => The device 'sysfs' could not be mounted in '${MOUNTPOINT_PREFIX}/sys'.\n"
  output "\r\rMount 'sys' [OK]\n"
}

function mount_storage () {
  local STORAGE_REALPATH="${1}"
  local STORAGE_BASENAME
  STORAGE_BASENAME=$(/usr/bin/basename "${STORAGE_REALPATH}")
  output "Mount '${STORAGE_REALPATH}2' ..."
  /usr/bin/install --directory "/mnt/${STORAGE_BASENAME}" || \
    error "\r\rMount '${STORAGE_REALPATH}p2' [ERROR]\n" \
          "  => The mount point '/mnt/${STORAGE_BASENAME}' could not be installed.\n"
  /usr/bin/mount --source "${STORAGE_REALPATH}2" \
                 --target "/mnt/${STORAGE_BASENAME}" || \
    error "\r\rMount '${STORAGE_REALPATH}2' [ERROR]\n" \
          "  => The device '${STORAGE_REALPATH}2' could not be mounted in '/mnt/${STORAGE_BASENAME}'.\n"
  output "\r\rMount '${STORAGE_REALPATH}2' [OK]\n"
  output "Mount '${STORAGE_REALPATH}1' ..."
  ESP_MOUNTPOINT=$(
    /usr/bin/grep '/boot' "/mnt/${STORAGE_BASENAME}/etc/fstab" | \
    /usr/bin/mawk '$1 !~ /^#/ && $2 ~ /^[/]/ {print $2}'
  ) || \
    error "\r\rMount '${STORAGE_REALPATH}1' [ERROR]\n" \
          "  => The mount point could not be determined from 'fstab'.\n"
  /usr/bin/install --directory "/mnt/${STORAGE_BASENAME}${ESP_MOUNTPOINT}" || \
    error "\r\rMount '${STORAGE_REALPATH}1' [ERROR]\n" \
          "  => The mount point '/mnt/${STORAGE_BASENAME}${ESP_MOUNTPOINT}' could not be installed.\n"
  /usr/bin/mount --source "${STORAGE_REALPATH}1" \
                 --target "/mnt/${STORAGE_BASENAME}${ESP_MOUNTPOINT}" || \
    error "\r\rMount '${STORAGE_REALPATH}1' [ERROR]\n" \
          "  => The device '${STORAGE_REALPATH}1' could not be mounted in '/mnt/${STORAGE_BASENAME}${ESP_MOUNTPOINT}'.\n"
  output "\r\rMount '${STORAGE_REALPATH}1' [OK]\n"
}

function mount_image () {
  local LOOP_DEVICE_REALPATH="${1}"
  local LOOP_DEVICE_BASENAME
  LOOP_DEVICE_BASENAME=$(/usr/bin/basename "${LOOP_DEVICE_REALPATH}")
  output "Prepare mount point for '${LOOP_DEVICE_REALPATH}p2' ..."
  /usr/bin/install --directory "/mnt/${LOOP_DEVICE_BASENAME}" || \
    error "\r\rPrepare mount point for '${LOOP_DEVICE_REALPATH}p2' [ERROR]\n" \
          "  => The mount point '/mnt/${LOOP_DEVICE_BASENAME}' could not be installed.\n"
  output "\r\rPrepare mount point for '${LOOP_DEVICE_REALPATH}p2' [OK]\n"
  output "Mount '${LOOP_DEVICE_REALPATH}p2' ..."
  /usr/bin/mount --source "${LOOP_DEVICE_REALPATH}p2" \
                 --target "/mnt/${LOOP_DEVICE_BASENAME}" || \
    error "\r\rMount '${LOOP_DEVICE_REALPATH}p2' [ERROR]\n" \
          "  => The device '${LOOP_DEVICE_REALPATH}p2' could not be mounted in '/mnt/${LOOP_DEVICE_BASENAME}'.\n"
  output "\r\rMount '${LOOP_DEVICE_REALPATH}p2' [OK]\n"
  output "Prepare mount point for '${LOOP_DEVICE_REALPATH}p1' ..."
  ESP_MOUNTPOINT=$(/usr/bin/grep '/boot' "/mnt/${LOOP_DEVICE_BASENAME}/etc/fstab" | /usr/bin/mawk '$1 !~ /^#/ && $2 ~ /^[/]/ {print $2}') || \
    error "\r\rPrepare mount point for '${LOOP_DEVICE_REALPATH}p1' [ERROR]\n" \
          "  => The mount point could not be determined from 'fstab'.\n"
  /usr/bin/install --directory "/mnt/${LOOP_DEVICE_BASENAME}${ESP_MOUNTPOINT}" || \
    error "\r\rPrepare mount point for '${LOOP_DEVICE_REALPATH}p1' [ERROR]\n" \
          "  => The mount point '/mnt/${LOOP_DEVICE_BASENAME}${ESP_MOUNTPOINT}' could not be installed.\n"
  output "\r\rPrepare mount point for '${LOOP_DEVICE_REALPATH}p1' [OK]\n"
  output "Mount '${LOOP_DEVICE_REALPATH}p1' ..."
  /usr/bin/mount --source "${LOOP_DEVICE_REALPATH}p1" \
                 --target "/mnt/${LOOP_DEVICE_BASENAME}${ESP_MOUNTPOINT}" || \
    error "\r\rMount '${LOOP_DEVICE_REALPATH}p1' [ERROR]\n" \
          "  => The device '${LOOP_DEVICE_REALPATH}p1' could not be mounted in '/mnt/${LOOP_DEVICE_BASENAME}${ESP_MOUNTPOINT}'.\n"
  output "\r\rMount '${LOOP_DEVICE_REALPATH}p1' [OK]\n"
}

function mount_template () {
  local TEMPLATE_REALPATH="${1}"
  local TEMPLATE_BASENAME
  TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
  output "Prepare mount point for '${TEMPLATE_BASENAME}' ..."
  /usr/bin/install --directory "/mnt/${TEMPLATE_BASENAME}" || \
    error "\r\rPrepare mount point for '${TEMPLATE_BASENAME}' [ERROR]\n" \
          "  => The mount point '/mnt/${TEMPLATE_BASENAME}' could not be installed.\n"
  output "\r\rPrepare mount point for '${TEMPLATE_BASENAME}' [OK]\n"
  output "Mount '${TEMPLATE_BASENAME}' ..."
  /usr/bin/mount --bind "${TEMPLATE_REALPATH}" \
                 --target "/mnt/${TEMPLATE_BASENAME}" || \
    error "\r\rMount '${TEMPLATE_BASENAME}' [ERROR]\n" \
          "  => The template '${TEMPLATE_BASENAME}' could not be mounted in '/mnt/${TEMPLATE_BASENAME}'.\n"
  output "\r\rMount '${TEMPLATE_BASENAME}' [OK]\n"
}

function output () {
  if [[ -n "${1:-}" ]]; then
    /usr/bin/echo -n -e "${1}"
  fi
}

function table_padding () {
  local WIDTH="${1}"
  shift
  local ELLIPSIS="${1:-}"
  /usr/bin/printf "%-*s" "${WIDTH}" "${ELLIPSIS}"
}

function umount_image_storage () {
  local IMAGE_STORAGE_REALPATH="${1}"
  local IMAGE_STORAGE_BASENAME
  IMAGE_STORAGE_BASENAME=$(/usr/bin/basename "${IMAGE_STORAGE_REALPATH}")
  declare -A MOUNTS_MAP
  while IFS= read -r MOUNTS; do
    local MOUNTS_LINE
    MOUNTS_LINE=$(/usr/bin/awk '{print $2}' <<< "${MOUNTS}")
    local MOUNTS_COUNT
    MOUNTS_COUNT=$(
      /usr/bin/tr --complement \
                  --delete '/' <<< "${MOUNTS_LINE}" | \
      /usr/bin/wc --chars
    )
    MOUNTS_MAP["$MOUNTS_COUNT"]+="$MOUNTS_LINE"$' '
  done <<< "$(/usr/bin/grep "${IMAGE_STORAGE_BASENAME}" '/proc/mounts' | \
             /usr/bin/grep --extended-regexp "${IMAGE_STORAGE_BASENAME}|devpts|devtmpfs|mqueue|proc|sys|tmpfs"
            )"
  local MOUNTS_COUNT
  for MOUNTS_COUNT in $(
    /usr/bin/tr '\n' ' ' <<< "${!MOUNTS_MAP[@]}" | \
    /usr/bin/sort --numeric-sort \
                  --reverse
  ); do
    local MOUNTPOINT
    for MOUNTPOINT in ${MOUNTS_MAP[$MOUNTS_COUNT]}; do
      STORAGE_REALPATH=$(
        /usr/bin/grep "${MOUNTPOINT}" '/proc/mounts' | \
        /usr/bin/awk '{print $1}'
      )
      output "Unmount '${STORAGE_REALPATH}' ..."
      /usr/bin/umount "${MOUNTPOINT}" || \
        error "\r\rUnmount '${STORAGE_REALPATH}' [ERROR]\n" \
              "  => The device '${STORAGE_REALPATH}' could not be unmounted from '${MOUNTPOINT}'\n"
      output "\r\rUnmount '${STORAGE_REALPATH}' [OK]\n"
    done
  done
  if [[ -d "/mnt/${IMAGE_STORAGE_BASENAME}" ]]; then
    output "Remove mountpoint '/mnt/${IMAGE_STORAGE_BASENAME}' ..."
    /usr/bin/rm --force \
                --dir "/mnt/${IMAGE_STORAGE_BASENAME}" || \
      error "\r\rRemove mountpoint '/mnt/${IMAGE_STORAGE_BASENAME}' [ERROR]\n" \
            "  => The mountpoint '/mnt/${IMAGE_STORAGE_BASENAME}' could not be removed.\n"
    output "\r\rRemove mountpoint '/mnt/${IMAGE_STORAGE_BASENAME}' [OK]\n"
  fi
}

function umount_template () {
  local TEMPLATE_REALPATH
  local TEMPLATE_BASENAME
  TEMPLATE_REALPATH="${1}"
  TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
  output "Unmount '${TEMPLATE_BASENAME}' ..."
  /usr/bin/umount --recursive "/mnt/${TEMPLATE_BASENAME}" || \
    error "\r\rUnmount '${TEMPLATE_BASENAME}' [ERROR]\n" \
          "  => The template '${TEMPLATE_BASENAME}' could not be unmounted from '/mnt/${TEMPLATE_BASENAME}'\n"
  output "\r\rUnmount '${TEMPLATE_BASENAME}' [OK]\n"
  if [[ -d "/mnt/${TEMPLATE_BASENAME}" ]]; then
    output "Remove mountpoint '/mnt/${TEMPLATE_BASENAME}' ..."
    /usr/bin/rm --force \
                --dir "/mnt/${TEMPLATE_BASENAME}" || \
      error "\r\rRemove mountpoint '/mnt/${TEMPLATE_BASENAME}' [ERROR]\n" \
            "  => The mountpoint '/mnt/${TEMPLATE_BASENAME}' could not be removed.\n"
    output "\r\rRemove mountpoint '/mnt/${TEMPLATE_BASENAME}' [OK]\n"
  fi
}


###
#
# Runtime Environment
#
###

/usr/bin/echo -e 'Raspberry Pi Image Tool\n'

while getopts ':a:c:d:g:him:u:' OPT; do
  case "${OPT}" in
    a)
      INPUT="${OPTARG}"
      TEMPLATE_REALPATH="${REPOSITORY_PATH}/${INPUT}"
      TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}") || \
        error "\r\rArchive Raspberry Pi template '${INPUT}' [ERROR]\n" \
              "  => A Raspberry Pi template with the name '${INPUT}' does not exists.\n"
      /usr/bin/test -f "${REPOSITORY_PATH}/${TEMPLATE_BASENAME}/.version" && \
        TEMPLATE_LOCAL_VERSION=$(/usr/bin/cat "${REPOSITORY_PATH}/${TEMPLATE_BASENAME}/.version")
      consistency "${TEMPLATE_REALPATH}"
      output "Archive Raspberry Pi template '${TEMPLATE_BASENAME}' ..."
      /usr/bin/tar --create \
                   --use-compress-program="/usr/bin/xz -9 --extreme" \
                   --file "./${TEMPLATE_BASENAME}.${TEMPLATE_LOCAL_VERSION}.tar.xz" \
                   --directory="${TEMPLATE_REALPATH}" '.' || \
        error "\r\rArchive Raspberry Pi template '${TEMPLATE_BASENAME}' [ERROR]\n" \
              "  => The Raspberry Pi template '${TEMPLATE_BASENAME}' could not be archived.\n"
      output "\r\rArchive Raspberry Pi template '${TEMPLATE_BASENAME}' [OK]\n"
      output "Generate Raspberry Pi archive hashsum ..."
      /usr/bin/sha512sum "./${TEMPLATE_BASENAME}.${TEMPLATE_LOCAL_VERSION}.tar.xz" | \
      /usr/bin/sed --expression='s, .*/,  ,' >> "./${TEMPLATE_BASENAME}.${TEMPLATE_LOCAL_VERSION}.tar.xz.sha512sum" || \
        error '\r\rGenerate Raspberry Pi archive hashsum [ERROR]\n' \
              '  => The hashsum for the Raspberry Pi archived template could not be generated.\n'
      output '\r\rGenerate Raspberry Pi archive hashsum [OK]\n'
      exit 0
    ;;
    c)
      INPUT="${OPTARG}"
      if [[ -d "${REPOSITORY_PATH}/${INPUT}" ]] && \
         [[ ! -d "./${INPUT}" ]]; then
        INPUT_REALPATH=$(/usr/bin/realpath "${REPOSITORY_PATH}/${INPUT}")
      else
        INPUT_REALPATH=$(/usr/bin/realpath "${INPUT}")
      fi
      consistency "${INPUT_REALPATH}"
      case "${CONSISTENCY_TYPE}" in
        IMAGE)
          if /usr/sbin/losetup --list \
                               --noheadings \
                               --output 'BACK-FILE' \
                               --raw | \
             /usr/bin/grep --quiet "${INPUT_REALPATH}"; then
            LOOP_DEVICE_REALPATH=$(
              /usr/sbin/losetup --list \
                                --noheadings \
                                --output 'NAME,BACK-FILE' \
                                --raw | \
              /usr/bin/grep "${INPUT_REALPATH}" | \
              /usr/bin/sed --expression="s| ${INPUT_REALPATH}||"
            )
          fi
          if [[ -z "${LOOP_DEVICE_REALPATH:-}" ]]; then
            loop_device_attach "${INPUT_REALPATH}"
            LOOP_DEVICE_BASENAME=$(/usr/bin/basename "${LOOP_DEVICE_REALPATH:-}")
            mount_image "${LOOP_DEVICE_REALPATH}"
            mount_api "${LOOP_DEVICE_REALPATH}"
            output "\n ... Open chroot environment in '/mnt/${LOOP_DEVICE_BASENAME}' ... \n\n"
            /usr/bin/sleep '3s'
            /usr/sbin/chroot "/mnt/${LOOP_DEVICE_BASENAME}" || \
              /usr/bin/true
            output "\n ... Close chroot environment from '/mnt/${LOOP_DEVICE_BASENAME}' ... \n\n"
            /usr/bin/sleep '3s'
            umount_image_storage "${LOOP_DEVICE_REALPATH}"
            loop_device_detach "${LOOP_DEVICE_REALPATH}"
          elif [[ -n "${LOOP_DEVICE_REALPATH}" ]]; then
            umount_image_storage "${LOOP_DEVICE_REALPATH}"
            loop_device_detach "${LOOP_DEVICE_REALPATH}"
            loop_device_attach "${INPUT_REALPATH}"
            LOOP_DEVICE_BASENAME=$(/usr/bin/basename "${LOOP_DEVICE_REALPATH:-}")
            mount_image "${LOOP_DEVICE_REALPATH}"
            mount_api "${LOOP_DEVICE_REALPATH}"
            output "\n ... Open chroot environment in '/mnt/${LOOP_DEVICE_BASENAME}' ... \n\n"
            /usr/bin/sleep '3s'
            /usr/sbin/chroot "/mnt/${LOOP_DEVICE_BASENAME}" || \
              /usr/bin/true
            output "\n ... Close chroot environment from '/mnt/${LOOP_DEVICE_BASENAME}' ... \n\n"
            /usr/bin/sleep '3s'
            umount_image_storage "${LOOP_DEVICE_REALPATH}"
            loop_device_detach "${LOOP_DEVICE_REALPATH}"
          fi
        ;;
        STORAGE)
          STORAGE_REALPATH="${INPUT_REALPATH}"
          STORAGE_BASENAME=$(/usr/bin/basename "${INPUT_REALPATH}")
          if /usr/bin/grep --quiet "${STORAGE_REALPATH}" '/proc/mounts'; then
            STORAGE_MOUNTED="1"
          fi
          if [[ -z "${STORAGE_MOUNTED:-}" ]]; then
            mount_storage "${STORAGE_REALPATH}"
            mount_api "${STORAGE_REALPATH}"
            output '\n ... Open chroot environment ... \n\n'
            /usr/bin/sleep '3s'
            /usr/sbin/chroot "/mnt/${STORAGE_BASENAME}" || \
              /usr/bin/true
            output '\n ... Close chroot environment ... \n\n'
            umount_image_storage "${STORAGE_REALPATH}"
          elif [[ -n "${STORAGE_MOUNTED}" ]]; then
            umount_image_storage "${STORAGE_REALPATH}"
            mount_storage "${STORAGE_REALPATH}"
            mount_api "${STORAGE_REALPATH}"
            output '\n ... Open chroot environment ... \n\n'
            /usr/bin/sleep '3s'
            /usr/sbin/chroot "/mnt/${STORAGE_BASENAME}" || \
              /usr/bin/true
            output '\n ... Close chroot environment ... \n\n'
            umount_image_storage "${STORAGE_REALPATH}"
          fi
        ;;
        TEMPLATE)
          TEMPLATE_REALPATH="${INPUT_REALPATH}"
          TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
          if /usr/bin/grep --word-regexp --quiet "${TEMPLATE_BASENAME}" '/proc/mounts'; then
            TEMPLATE_MOUNTED="1"
          fi
          if [[ -z "${TEMPLATE_MOUNTED:-}" ]]; then
            mount_template "${TEMPLATE_REALPATH}"
            mount_api "${TEMPLATE_REALPATH}"
            output '\n ... Open chroot environment ... \n\n'
            /usr/bin/sleep '3s'
            /usr/sbin/chroot "/mnt/${TEMPLATE_BASENAME}" || \
              /usr/bin/true
            output '\n ... Close chroot environment ... \n\n'
            umount_template "${TEMPLATE_REALPATH}"
          elif [[ -n "${TEMPLATE_MOUNTED}" ]]; then
            for PID in $(/usr/bin/ps -e -o 'pid='); do
              if [[ -e "/proc/${PID}/root" ]] && \
                 [[ "$(/usr/bin/readlink --canonicalize /proc/${PID}/root)" == "/mnt/${TEMPLATE_BASENAME}" ]]; then
                error "\r\rOpen chroot environment for '${TEMPLATE_BASENAME}' [ERROR]\n" \
                      "  => There is already a chroot envrionment for the Raspberry Pi template '${TEMPLATE_BASENAME}' opened.\n"
              fi
            done
            umount_template "${TEMPLATE_REALPATH}"
            mount_template "${TEMPLATE_REALPATH}"
            mount_api "${TEMPLATE_REALPATH}"
            output '\n ... Open chroot environment ... \n\n'
            /usr/bin/sleep '3s'
            /usr/sbin/chroot "/mnt/${TEMPLATE_BASENAME}" || \
              /usr/bin/true
            output '\n ... Close chroot environment ... \n\n'
            umount_template "${TEMPLATE_REALPATH}"
          fi
        ;;
        *)
          error '\r\rCheck the input for consistency [ERROR]\n' \
                '  => A chroot environment could only be opened on a Raspberry Pi image or storage.\n'
        ;;
      esac
      exit 0
    ;;
    d)
      INPUT="${OPTARG}"
      TMP=$(/usr/bin/mktemp --directory --quiet)
      trap '/usr/bin/rm --force --recursive ${TMP}' EXIT
      TEMPLATE_BASENAME="${INPUT}"
      TEMPLATE_REALPATH="${REPOSITORY_PATH}/${TEMPLATE_BASENAME}"
      TEMPLATE_ONLINE_VERSION=$(
        /usr/bin/wget --quiet --output-document='-' "${REPOSITORY_URL}" 2> '/dev/null' | \
        /usr/bin/tr '\n' ' ' | \
        /usr/bin/sed --expression='s/</\n</g' | \
        /usr/bin/grep --ignore-case --extended-regexp '^<a ' | \
        /usr/bin/sed --quiet --regexp-extended 's/.*href=["'"'"']?([^"'"'"' >]+).*/\1/p' | \
        /usr/bin/sed --expression='s,^\./,,; s/\?.*$//' | \
        /usr/bin/grep --invert-match --extended-regexp '^(\.\./|/)$' | \
        /usr/bin/grep --invert-match '/$' | \
        /usr/bin/sed --expression='s/%20/ /g' | \
        /usr/bin/uniq | \
        /usr/bin/grep --extended-regexp "${TEMPLATE_BASENAME}\.[0-9]+\.tar\.xz$"
      )
      TEMPLATE_ONLINE_VERSION=$(/usr/bin/awk -F '.' '{print $NF}' <<< "${TEMPLATE_ONLINE_VERSION%.tar.xz}")
      /usr/bin/test -f "${TEMPLATE_REALPATH}/.version" && \
        TEMPLATE_LOCAL_VERSION=$(/usr/bin/cat "${TEMPLATE_REALPATH}/.version")
      if [[ ! -d "${TEMPLATE_REALPATH}" ]]; then
        image_download "${TEMPLATE_BASENAME}"
      elif [[ -d "${TEMPLATE_REALPATH}" ]] && \
           [[ "${TEMPLATE_LOCAL_VERSION}" -lt "${TEMPLATE_ONLINE_VERSION}" ]]; then
        output "Remove Raspberry Pi template '${TEMPLATE_BASENAME}' ..."
        /usr/bin/rm --force --recursive "${TEMPLATE_REALPATH}" || \
          error "\r\rRemove Raspberry Pi template '${TEMPLATE_BASENAME}' [ERROR]\n" \
                "  => The Raspberry Pi template archive under '${TEMPLATE_REALPATH}' could not be removed.\n"
        output "\r\rRemove Raspberry Pi template '${TEMPLATE_BASENAME}' [OK]\n"
        image_download "${TEMPLATE_BASENAME}"
      elif [[ -d "${TEMPLATE_REALPATH}" ]] && \
           [[ "${TEMPLATE_LOCAL_VERSION}" -gt "${TEMPLATE_ONLINE_VERSION}" ]]; then
        read -r -p $'The Raspberry Pi template in the local repository is newer\nthan the template in the online repository.\n\nWould you like to overwrite the local template? [Yes/No] ' ANSWER
        if [[ "${ANSWER,,}" == "yes" ]]; then
          output "Remove Raspberry Pi template '${TEMPLATE_BASENAME}' ..."
          /usr/bin/rm --force --recursive "${TEMPLATE_REALPATH}" || \
            error "\r\rRemove Raspberry Pi template '${TEMPLATE_BASENAME}' [ERROR]\n" \
                  "  => The Raspberry Pi template under '${TEMPLATE_REALPATH}' could not be removed.\n"
          output "\r\rRemove Raspberry Pi template '${TEMPLATE_BASENAME}' [OK]\n"
          image_download "${TEMPLATE_BASENAME}"
        else
          exit 0
        fi
      elif [[ -d "${TEMPLATE_REALPATH}" ]] && \
           [[ "${TEMPLATE_LOCAL_VERSION}" -eq "${TEMPLATE_ONLINE_VERSION}" ]]; then
            error "The Raspberry Pi template '${TEMPLATE_BASENAME}' is already up to date.\n"
      fi
      exit 0
    ;;
    g)
      INPUT="${OPTARG}"
      TMP=$(/usr/bin/mktemp --directory --quiet)
      trap '/usr/bin/rm --force --recursive ${TMP}' EXIT
      TEMPLATE_BASENAME="${OPTARG}"
      TEMPLATE_REALPATH="${REPOSITORY_PATH}/${TEMPLATE_BASENAME}"
      if [[ ! -d "${TEMPLATE_REALPATH}" ]]; then
        image_download "${TEMPLATE_BASENAME}"
      fi
      TEMPLATE_LOCAL_VERSION=$(/usr/bin/cat "${TEMPLATE_REALPATH}/.version")
      /usr/bin/test -f "./${TEMPLATE_BASENAME}.${TEMPLATE_LOCAL_VERSION}.img" && \
        error '\r\rGenerate Raspberry Pi image [ERROR]\n' \
              "  => A Raspberry Pi image with the name '${TEMPLATE_BASENAME}.${TEMPLATE_LOCAL_VERSION}.img' already exists.\n"
      consistency "${TEMPLATE_REALPATH}"
      SYSTEMD_REPART_CONFIGURATION_PATH="${TMP}/repart.d"
      SYSTEMD_REPART_ESP_CONFIGURATION_PATH="${TMP}/repart.d/00-esp.conf"
      SYSTEMD_REPART_ROOT_CONFIGURATION_PATH="${TMP}/repart.d/25-root.conf"
      SYSTEMD_REPART_IMAGE_OUTPUT=$(/usr/bin/pwd --physical)
      output 'Prepare partition layout & file system configuration ...'
      /usr/bin/install --directory "${SYSTEMD_REPART_CONFIGURATION_PATH}" || \
        error '\r\rPrepare partition layout & file system configuration [ERROR]\n' \
              "  => The configuration directory '${SYSTEMD_REPART_CONFIGURATION_PATH}' could not be installed.\n"
      configuration "${SYSTEMD_REPART_ESP_CONFIGURATION_PATH}"
      /usr/bin/test -f "${SYSTEMD_REPART_ESP_CONFIGURATION_PATH}" || \
        error '\r\rPrepare partition layout & file system configuration [ERROR]\n' \
              "  => The configuration file '${SYSTEMD_REPART_ESP_CONFIGURATION_PATH}' could not be created.\n"
      configuration "${SYSTEMD_REPART_ROOT_CONFIGURATION_PATH}"
      /usr/bin/test -f "${SYSTEMD_REPART_ROOT_CONFIGURATION_PATH}" || \
        error '\r\rPrepare partition layout & file system configuration [ERROR]\n' \
              "  => The configuration file '${SYSTEMD_REPART_ROOT_CONFIGURATION_PATH}' could not be created.\n"
      output '\r\rPrepare partition layout & file system configuration [OK]\n'
      output 'Prepare Raspberry Pi image configuration ...'
      configuration "${TMP}/image.conf"
      /usr/bin/test -f "${TMP}/image.conf" || \
        error '\r\rPrepare Raspberry Pi image configuration [ERROR]\n' \
              "  => The configuration file '${TMP}/image.conf' could not be created.\n"
      /usr/bin/editor "${TMP}/image.conf" || \
        error '\r\rPrepare Raspberry Pi image configuration [ERROR]\n' \
              "  => The configuration file '${TMP}/image.conf' could not be modified.\n"
      output '\r\rPrepare Raspberry Pi image configuration [OK]\n'
      output 'Initialize Raspberry Pi image configuration ...'
      source "${TMP}/image.conf" || \
        error '\r\rInitialize Raspberry Pi image configuration [ERROR]\n' \
              "  => The configuration file '${TMP}/image.conf' could not be initialized.\n"
      /usr/bin/test -n "${ROOT_PASSWORD}" || \
        error '\r\rInitialize Raspberry Pi image configuration [ERROR]\n' \
              "  => The password for user 'root' ist not configured.\n"
      output '\r\rInitialize Raspberry Pi image configuration [OK]\n'
      output 'Generate Raspberry Pi image ...'
      IMAGE_SIZE=$(
        /usr/bin/du -shm "${TEMPLATE_REALPATH}" | \
        /usr/bin/cut --fields='1'
      )
      IMAGE_REALPATH="${SYSTEMD_REPART_IMAGE_OUTPUT:='/dev/null'}/${TEMPLATE_BASENAME}.${TEMPLATE_LOCAL_VERSION}.img"
      /usr/bin/systemd-repart --empty='create' \
                              --seed='random' \
                              --sector-size='512' \
                              --size="$(( IMAGE_SIZE * 2 ))M" \
                              --definitions="${SYSTEMD_REPART_CONFIGURATION_PATH}" \
                              --copy-source="${TEMPLATE_REALPATH}" \
                              "${IMAGE_REALPATH}" &> '/dev/null' || \
        error '\r\rGenerate Raspberry Pi image [ERROR]\n' \
              "  => The Raspberry Pi image '${IMAGE_REALPATH}' could not be generated.\n"
      output '\r\rGenerate Raspberry Pi image [OK]\n'
      loop_device_attach "${IMAGE_REALPATH}"
      LOOP_DEVICE_BASENAME=$(/usr/bin/basename "${LOOP_DEVICE_REALPATH}")
      mount_image "${LOOP_DEVICE_REALPATH}"
      mount_api "${LOOP_DEVICE_REALPATH}"
      output "Set new file system UUID's in static filesystem table"
      LOOP_DEVICE_ROOT_PARTUUID=$(/usr/sbin/blkid --output 'value' --match-tag 'PARTUUID' "${LOOP_DEVICE_REALPATH}p2")
      LOOP_DEVICE_ESP_UUID=$(/usr/sbin/blkid --output 'value' --match-tag 'UUID' "${LOOP_DEVICE_REALPATH}p1")
      LOOP_DEVICE_ROOT_UUID=$(/usr/sbin/blkid --output 'value' --match-tag 'UUID' "${LOOP_DEVICE_REALPATH}p2")
      /usr/bin/sed --in-place \
                   --regexp-extended \
                   --expression="0,/(UUID=[0-9A-F-]{9})/s//UUID=${LOOP_DEVICE_ESP_UUID}/" \
                   --expression="0,/(UUID=[0-9a-fA-F-]{36})/s//UUID=${LOOP_DEVICE_ROOT_UUID}/" \
                   "/mnt/${LOOP_DEVICE_BASENAME}/etc/fstab" || \
        error "\r\rSet new file system UUID's in static filesystem table [ERROR]\n" \
              "  => The file system UUID's could not be written to '/mnt/${LOOP_DEVICE_BASENAME}/etc/fstab'.\n"
      output "\r\rSet new file system UUID's in static filesystem table [OK]\n"
      output "Set new partition UUID in kernel's command-line parameters"
      /usr/bin/sed --follow-symlinks \
                   --in-place \
                   --regexp-extended \
                   --expression="0,/(PARTUUID=[0-9a-fA-F-]{36})/s//PARTUUID=${LOOP_DEVICE_ROOT_PARTUUID}/" \
                   "/mnt/${LOOP_DEVICE_BASENAME}/boot/cmdline.txt" || \
        error "\r\rSet new partition UUID in kernel's command-line parameters [ERROR]\n" \
              "  => The partition UUID could not be written to '/mnt/${LOOP_DEVICE_BASENAME}/boot/cmdline.txt'.\n"
      output "\r\rSet new partition UUID in kernel's command-line parameters [OK]\n"
      output 'Create initial ram file system ...'
      initramfs_chroot "/mnt/${LOOP_DEVICE_BASENAME}" &> '/dev/null'
      output '\r\rCreate initial ram file system [OK]\n'
      output 'Set the hostname'
      if [[ -n "${HOSTNAME}" ]]; then
        HOSTNAME_OLD=$(/usr/bin/cat "/mnt/${LOOP_DEVICE_BASENAME}/etc/hostname")
        mapfile -t HOSTNAME_FILES < <(/usr/bin/grep --recursive --files-with-matches "${HOSTNAME_OLD}" "/mnt/${LOOP_DEVICE_BASENAME}/etc")
        for FILE in "${HOSTNAME_FILES[@]}"; do
          /usr/bin/sed --in-place "s/${HOSTNAME_OLD}/${HOSTNAME}/g" "${FILE}" || \
            error '\r\rSet the hostname [ERROR]\n' \
                  "  => The hostname '${HOSTNAME}' could not be set in '${FILE}'.\n"
        done
      fi
      output '\r\rSet the hostname [OK]\n'
      output "Set password for user 'root'"
      /usr/bin/echo 'root':"${ROOT_PASSWORD}" | /usr/sbin/chpasswd --root "/mnt/${LOOP_DEVICE_BASENAME}" || \
        error "\r\rSet password for user 'root' [ERROR]\n" \
              "  => The password for user 'root' could not be set.\n"
      output "\r\rSet password for user 'root' [OK]\n"
      if [[ -n "${SSH_PUBKEY}" ]]; then
        output "Set Secure Shell Public Key for user 'root'"
        /usr/bin/echo "${SSH_PUBKEY}" > "/mnt/${LOOP_DEVICE_BASENAME}/etc/ssh/authorizedkeys.d/root"
        /usr/bin/chmod '0600' "/mnt/${LOOP_DEVICE_BASENAME}/etc/ssh/authorizedkeys.d/root" || \
          error "\r\rSet Secure Shell Public Key for user 'root' [ERROR]\n" \
                "  => The Secure Shell Public Key could not be written to '/mnt/${LOOP_DEVICE_BASENAME}/etc/ssh/authorizedkeys.d/root'.\n"
        output "\r\rSet Secure Shell Public Key for user 'root' [OK]\n"
      fi
      if [[ -n "${WIFI_SSID}" ]] && \
         [[ -n "${WIFI_PASSWORD}" ]] && \
         [[ -n "${WIFI_COUNTRY}" ]] && \
         [[ -n "${WIFI_KEY_MGMT}" ]] && \
         [[ -n "${WIFI_SCAN_SSID}" ]]; then
        output 'Set Wi-Fi configuration'
        /usr/bin/sed --in-place \
                     --expression="s/country=.*/country=${WIFI_COUNTRY}/" \
                     "/mnt/${LOOP_DEVICE_BASENAME}/etc/wpa_supplicant/wpa_supplicant.conf" || \
          error '\r\rSet Wi-Fi configuration [ERROR]\n' \
                "  => The configuration 'country=${WIFI_COUNTRY}' could not be set.\n"
        case "${WIFI_KEY_MGMT}" in
          WPA-PSK)
            output '\r\rSet Wi-Fi configuration [OK]\n'
            /usr/lib/raspi-image/wpa_passphrase "${WIFI_SSID}" "${WIFI_PASSWORD}" | \
            /usr/bin/sed --expression='3d' \
                         --expression='5 ikey_mgmt=\' \
                         --expression='scan_ssid=\' | \
            /usr/bin/sed --expression="s/key_mgmt=/$(/usr/bin/printf '\t')key_mgmt=WPA-PSK/" \
                         --expression="s/scan_ssid=/$(/usr/bin/printf '\t')scan_ssid=${WIFI_SCAN_SSID}/g" \
                         >> "/mnt/${LOOP_DEVICE_BASENAME}/etc/wpa_supplicant/wpa_supplicant.conf" || \
              error '\r\rCreate Wi-Fi profile configuration [ERROR]\n' \
                    "  => The Wi-Fi profile configuration could not be written to '/mnt/${LOOP_DEVICE_BASENAME}/etc/wpa_supplicant/wpa_supplicant.conf'.\n"
            output '\r\rCreate Wi-Fi profile configuration [OK]\n'
          ;;
          SAE)
            output '\r\rSet Wi-Fi configuration [OK]\n'
            output 'Create Wi-Fi profile configuration'
            /usr/lib/raspi-image/wpa_passphrase "${WIFI_SSID}" "${WIFI_PASSWORD}" | \
            /usr/bin/sed --expression='4d' \
                         --expression='5 ikey_mgmt=\' \
                         --expression='proto=\' \
                         --expression='scan_ssid=\' | \
            /usr/bin/sed --expression="s/#psk=/sae_password=/" \
                         --expression="s/key_mgmt=/$(/usr/bin/printf '\t')key_mgmt=SAE/" \
                         --expression="s/proto=/$(/usr/bin/printf '\t')proto=RSN/" \
                         --expression="s/scan_ssid=/$(/usr/bin/printf '\t')scan_ssid=${WIFI_SCAN_SSID}/g" \
                         >> "/mnt/${LOOP_DEVICE_BASENAME}/etc/wpa_supplicant/wpa_supplicant.conf" || \
              error '\r\rCreate Wi-Fi profile configuration [ERROR]\n' \
                    "  => The Wi-Fi profile configuration could not be written to '/mnt/${LOOP_DEVICE_BASENAME}/etc/wpa_supplicant/wpa_supplicant.conf'.\n"
            output '\r\rCreate Wi-Fi profile configuration [OK]\n'
          ;;
        esac
      fi
      if [[ "${CERTIFICATE}" == 'true' ]] && \
         [[ -n "${RSA_KEYSIZE}" ]] && \
         [[ -n "${ECDSA_KEYSIZE}" ]] && \
         [[ -n "${COUNTRY}" ]] && \
         [[ -n "${STATE}" ]] && \
         [[ -n "${LOCALITY}" ]] && \
         [[ -n "${ORGANIZATION}" ]] && \
         [[ -n "${COMMON_NAME}" ]]; then
        output 'Generate RSA certificate'
        /usr/bin/openssl req -x509 -nodes -days '360' -newkey "rsa:${RSA_KEYSIZE}" \
                                                      -keyout "/mnt/${LOOP_DEVICE_BASENAME}/etc/ssl/key/root.rsa.key" \
                                                      -out "/mnt/${LOOP_DEVICE_BASENAME}/etc/ssl/crt/root.rsa.crt" \
                                                      -subj "/C=${COUNTRY}/ST=${STATE}/L=${LOCALITY}/O=${ORGANIZATION}/OU=${ORGANIZATIONAL_UNIT}/CN=${COMMON_NAME}" &> '/dev/null' || \
          error '\r\rGenerate RSA certificate [ERROR]\n' \
                '  => The certificate could not be generated.\n'
        output '\r\rGenerate RSA certificate [OK]\n'
        output 'Generate ECDSA certificate'
        /usr/bin/openssl req -x509 -nodes -days '360' -newkey 'ec' \
                                                      -pkeyopt "ec_paramgen_curve:${ECDSA_KEYSIZE}" \
                                                      -keyout "/mnt/${LOOP_DEVICE_BASENAME}/etc/ssl/key/root.ecc.key" \
                                                      -out  "/mnt/${LOOP_DEVICE_BASENAME}/etc/ssl/crt/root.ecc.crt" \
                                                      -subj "/C=${COUNTRY}/ST=${STATE}/L=${LOCALITY}/O=${ORGANIZATION}/OU=${ORGANIZATIONAL_UNIT}/CN=${COMMON_NAME}" &> '/dev/null' || \
          error '\r\rGenerate ECDSA certificate [ERROR]\n' \
                '  => The certificate could not be generated.\n'
        output '\r\rGenerate ECDSA certificate [OK]\n'
      fi
      if [[ "${DHPEM}" == 'true' ]] && \
         [[ -n "${DHPEM_SIZE}" ]]; then
        for SIZE in ${DHPEM_SIZE}; do
          output "Generate ${SIZE}-bit DHPEM"
          /usr/bin/openssl dhparam -out "/mnt/${LOOP_DEVICE_BASENAME}/etc/ssl/dhpem/dh${SIZE}.pem" -2 "${SIZE}" &> '/dev/null' || \
            error "\r\rGenerate ${SIZE}-bit DHPEM [ERROR]\n" \
                  "  => The ${SIZE}-bit DHPEM file could not be generated.\n"
          output "\r\rGenerate ${SIZE}-bit DHPEM [OK]\n"
        done
      fi
      output 'Cleanup Raspberry Pi template files ...'
      /usr/bin/rm "/mnt/${LOOP_DEVICE_BASENAME}/.cleanup" || \
        error '\r\rCleanup Raspberry Pi template files [ERROR]\n' \
              "  => The Raspberry Pi template file '/mnt/${LOOP_DEVICE_BASENAME}/.cleanup' could not be removed.\n"
      /usr/bin/rm "/mnt/${LOOP_DEVICE_BASENAME}/.version" || \
        error '\r\rCleanup Raspberry Pi template files [ERROR]\n' \
              "  => The Raspberry Pi template file '/mnt/${LOOP_DEVICE_BASENAME}/.version' could not be removed.\n"
      output '\r\rCleanup Raspberry Pi template files [OK]\n'
      umount_image_storage "${LOOP_DEVICE_REALPATH}"
      output 'Adjust Raspberry Pi image file system'
      /usr/sbin/tune2fs -e 'remount-ro' "${LOOP_DEVICE_REALPATH}p2" &> '/dev/null' || \
        error '\r\rAdjust Raspberry Pi image file system [ERROR]\n' \
              '  => The error behavior of the image file system could not be set.\n'
      /usr/sbin/tune2fs -M '/' "${LOOP_DEVICE_REALPATH}p2" &> '/dev/null' || \
        error '\r\rAdjust Raspberry Pi image file system [ERROR]\n' \
              '  => The last mount point of the image file system could not be set.\n'
      output '\r\rAdjust Raspberry Pi image file system [OK]\n'
      image_fsck "${LOOP_DEVICE_REALPATH}"
      loop_device_detach "${LOOP_DEVICE_REALPATH}"
      exit 0
    ;;
    i)
      if [[ -d "${REPOSITORY_PATH}" ]]; then
        mapfile -t REPOSITORY_LOCAL_FILES < <(
          /usr/bin/find "${REPOSITORY_PATH}/" -mindepth '1' -maxdepth '1' -type 'd' ! -name 'lost+found' -exec /usr/bin/basename {} \;
        )
      fi
      mapfile -t REPOSITORY_ONLINE_FILES < <(
        /usr/bin/wget --quiet --output-document='-' "${REPOSITORY_URL}" 2> '/dev/null' | \
        /usr/bin/tr '\n' ' ' | \
        /usr/bin/sed --expression='s/</\n</g' | \
        /usr/bin/grep --ignore-case --extended-regexp '^<a ' | \
        /usr/bin/sed --quiet --regexp-extended 's/.*href=["'"'"']?([^"'"'"' >]+).*/\1/p' | \
        /usr/bin/sed --expression='s,^\./,,; s/\?.*$//' | \
        /usr/bin/grep --invert-match --extended-regexp '^(\.\./|/)$' | \
        /usr/bin/grep --invert-match '/$' | \
        /usr/bin/sed --expression='s/%20/ /g' | \
        /usr/bin/uniq | \
        /usr/bin/grep --extended-regexp '\.tar\.xz$'
      )
      REPOSITORY_FILES_PIPE=("${REPOSITORY_LOCAL_FILES[@]}" "${REPOSITORY_ONLINE_FILES[@]}")
      mapfile -t REPOSITORY_FILES_PIPE < <(
        /usr/bin/printf '%s\n' "${REPOSITORY_FILES_PIPE[@]}" | \
        /usr/bin/sort --key='1,1' \
                      --key='2,2r' \
                      --field-separator='.'
      )
      for REPOSITORY_FILE in "${REPOSITORY_FILES_PIPE[@]}"; do
        REPOSITORY_FILES+=("${REPOSITORY_FILE%.tar.xz}")
      done
      /usr/bin/printf ' '
      /usr/bin/printf '%.0s-' {1..108}
      /usr/bin/printf "\n| %-40s | %-30s | %-30s | %s\n" "Raspberry Pi Template" "Verion (Local Repository)" "Version (Online Repository)"
      /usr/bin/printf ' '
      /usr/bin/printf "%.0s-" {1..108}
      /usr/bin/printf "\n"
      for REPOSITORY_FILE in "${REPOSITORY_FILES[@]}"; do
        REPOSITORY_TEMPLATE="${REPOSITORY_FILE//.[0-9]*/}"
        for REPOSITORY_TEMPLATE_DIFF in "${REPOSITORY_LOOP[@]:-}"; do
          if [[ "$REPOSITORY_TEMPLATE" == "$REPOSITORY_TEMPLATE_DIFF" ]]; then
            REPOSITORY_LOOP_BREAK='1'
            break
          fi
        done
        if [[ "${REPOSITORY_LOOP_BREAK:-}" -eq '1' ]]; then
          unset REPOSITORY_LOOP_BREAK
          continue
        fi
        REPOSITORY_LOOP+=("${REPOSITORY_TEMPLATE}")
        if [[ "${REPOSITORY_FILE}" =~ \.([0-9]+) ]]; then
          REPOSITORY_ONLINE_VERSION=$(/usr/bin/awk -F '.' '{print $NF}' <<< "${REPOSITORY_FILE}")
        fi
        if [[ -d "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}" ]] && \
           [[ -s "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}/.version" ]]; then
          REPOSITORY_LOCAL_VERSION=$(/usr/bin/cat "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}/.version")
        elif [[ -d "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}" ]] && \
             [[ ! -s "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}/.version" ]]; then
          REPOSITORY_LOCAL_VERSION='N/V'
        fi
        REPOSITORY_TEMPLATE_PADDING=$(table_padding '40' "${REPOSITORY_TEMPLATE:-}")
        REPOSITORY_LOCAL_VERSION_PADDING=$(table_padding '30' "${REPOSITORY_LOCAL_VERSION=N/A}")
        REPOSITORY_ONLINE_VERSION_PADDING=$(table_padding '30' "${REPOSITORY_ONLINE_VERSION:=N/A}")
        REPOSITORY_TABLE_FORMAT_DIM=$'\e[2m'
        REPOSITORY_TABLE_FORMAT_GREEN=$'\e[92m'
        REPOSITORY_TABLE_FORMAT_RED=$'\e[91m'
        REPOSITORY_TABLE_FORMAT_RESET=$'\e[0m'
        if [[ -n "${REPOSITORY_LOCAL_VERSION:-}" ]] && \
           [[ -n "${REPOSITORY_ONLINE_VERSION:-}" ]]; then
          if [[ "${REPOSITORY_LOCAL_VERSION:-}" -lt "${REPOSITORY_ONLINE_VERSION:-}" ]]; then
            REPOSITORY_LOCAL_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_RED}${REPOSITORY_LOCAL_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
            REPOSITORY_ONLINE_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_GREEN}${REPOSITORY_ONLINE_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
          elif [[ "${REPOSITORY_LOCAL_VERSION:-}" -gt "${REPOSITORY_ONLINE_VERSION:-}" ]]; then
            REPOSITORY_LOCAL_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_GREEN}${REPOSITORY_LOCAL_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
            REPOSITORY_ONLINE_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_RED}${REPOSITORY_ONLINE_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
          fi
        fi
        if [[ "${REPOSITORY_LOCAL_VERSION:-}" == 'N/V' ]] || \
           [[  "${REPOSITORY_LOCAL_VERSION:-}" == 'N/A' ]] || \
           [[ -z "${REPOSITORY_LOCAL_VERSION:-}" ]]; then
          REPOSITORY_LOCAL_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_DIM}${REPOSITORY_LOCAL_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
        fi
        if [[ -z "${REPOSITORY_ONLINE_VERSION:-}" ]]; then
          REPOSITORY_ONLINE_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_DIM}${REPOSITORY_ONLINE_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
        fi
        /usr/bin/printf '| %s | %s | %s | %s\n' \
                        "${REPOSITORY_TEMPLATE_PADDING:-}" \
                        "${REPOSITORY_LOCAL_VERSION_PADDING:-}" \
                        "${REPOSITORY_ONLINE_VERSION_PADDING:-}"
        unset REPOSITORY_ONLINE_VERSION
        unset REPOSITORY_LOCAL_VERSION
      done
      /usr/bin/printf ' '
      /usr/bin/printf "%.0s-" {1..108}
      /usr/bin/printf "\n"
      exit 0
    ;;
    m)
      INPUT="${OPTARG}"
      if [[ -d "${REPOSITORY_PATH}/${INPUT}" ]] && \
         [[ ! -d "./${INPUT}" ]]; then
        INPUT_REALPATH=$(/usr/bin/realpath "${REPOSITORY_PATH}/${INPUT}")
      else
        INPUT_REALPATH=$(/usr/bin/realpath "${INPUT}")
      fi
      consistency "${INPUT_REALPATH}"
      case "${CONSISTENCY_TYPE}" in
        IMAGE)
          if /usr/sbin/losetup --list \
                               --noheadings \
                               --output 'BACK-FILE' \
                               --raw | \
             /usr/bin/grep --quiet "${INPUT_REALPATH}"; then
            LOOP_DEVICE_REALPATH=$(
              /usr/sbin/losetup --list \
                                --noheadings \
                                --output 'NAME,BACK-FILE' \
                                --raw | \
              /usr/bin/grep "${INPUT_REALPATH}" | \
              /usr/bin/sed --expression="s| ${INPUT_REALPATH}||"
            )
          fi
          if [[ -z "${LOOP_DEVICE_REALPATH:-}" ]]; then
            loop_device_attach "${INPUT_REALPATH}"
            mount_image "${LOOP_DEVICE_REALPATH}"
          elif [[ -n "${LOOP_DEVICE_REALPATH}" ]]; then
            umount_image_storage "${LOOP_DEVICE_REALPATH}"
            loop_device_detach "${LOOP_DEVICE_REALPATH}"
          fi
        ;;
        STORAGE)
          STORAGE_REALPATH="${INPUT_REALPATH}"
          if /usr/bin/grep --quiet "${STORAGE_REALPATH}" '/proc/mounts'; then
            STORAGE_MOUNTED="1"
          fi
          if [[ -z "${STORAGE_MOUNTED:-}" ]]; then
            mount_storage "${STORAGE_REALPATH}"
          elif [[ -n "${STORAGE_MOUNTED}" ]]; then
            umount_image_storage "${STORAGE_REALPATH}"
          fi
        ;;
        TEMPLATE)
          TEMPLATE_REALPATH="${INPUT_REALPATH}"
          TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
          if /usr/bin/grep --quiet "${TEMPLATE_BASENAME}" '/proc/mounts'; then
            TEMPLATE_MOUNTED="1"
          fi
          if [[ -z "${TEMPLATE_MOUNTED:-}" ]]; then
            mount_template "${TEMPLATE_REALPATH}"
          elif [[ -n "${TEMPLATE_MOUNTED}" ]]; then
            umount_template "${TEMPLATE_REALPATH}"
          fi
        ;;
        *)
          error '\r\rCheck the input for consistency [ERROR]\n' \
                '  => Only a Raspberry Pi image, storage or template could be mounted.\n'
        ;;
      esac
      exit 0
    ;;
    u)
      INPUT="${OPTARG}"
      TEMPLATE_REALPATH=$(/usr/bin/realpath "${REPOSITORY_PATH}/${INPUT}")
      TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
      TMP=$(/usr/bin/mktemp --directory --quiet)
      trap '/usr/bin/rm --force --recursive ${TMP}' EXIT
      /usr/bin/grep --quiet "${TEMPLATE_REALPATH}" '/proc/mounts' && \
        error "\r\rUpdate version of Raspberry Pi template '${TEMPLATE_BASENAME}' [ERROR]\n" \
              "  => The Raspberry Pi template '${TEMPLATE_BASENAME}' is mounted.\n"
      consistency "${TEMPLATE_REALPATH}"
      output 'Load configuration for the Raspberry Pi template cleanup ...'
      if [[ -f "${TEMPLATE_REALPATH}/.cleanup" ]]; then
        /usr/bin/cp --dereference "${TEMPLATE_REALPATH}/.cleanup" "${TMP}/cleanup" || \
          error '\r\rLoad configuration for the Raspberry Pi template cleanup [ERROR]\n' \
                "  => The configuration template '{TEMPLATE_REALPATH}/.cleanup' could not be copied to '{TMP}/cleanup'.\n"
      else
        configuration "${TMP}/cleanup" || \
          error '\r\rLoad configuration for the Raspberry Pi template cleanup [ERROR]\n' \
                "  => The default configuration template could not be copied to '{TMP}/cleanup'.\n"
      fi
      output '\r\rLoad configuration for the Raspberry Pi template cleanup [OK]\n'
      output 'Remove configured files and folders ...'
      mapfile -t TEMPLATE_REMOVE_LIST < <(
        /usr/bin/sed '/#.*/d' < "${TMP}/cleanup" | \
        while read -r TEMPLATE_REMOVE_PATH; do \
          /usr/bin/echo "${TEMPLATE_REALPATH}${TEMPLATE_REMOVE_PATH}"; \
        done
      )
      for X in "${TEMPLATE_REMOVE_LIST[@]}"; do
        /usr/bin/sh -c "/usr/bin/ls --directory ${X} | /usr/bin/xargs /usr/bin/rm --force --recursive" &> '/dev/null' || \
          error '\r\rRemove configured files and folders [ERROR]\n' \
                "  => The file or directory '${X}' could not be removed.\n"
      done
      output '\r\rRemove configured files and folders [OK]\n'
      TEMPLATE_NEW_VERSION=$(/usr/bin/date +%s)
      output "Update Raspberry Pi template verion to '${TEMPLATE_NEW_VERSION}' ..."
      configuration "${TEMPLATE_REALPATH}/.version" || \
        error "\r\rUpdate Raspberry Pi template verion to '${TEMPLATE_NEW_VERSION}' [ERROR]\n" \
              "  => The template version cloud not be updated.\n"
      output "\r\rUpdate Raspberry Pi template verion to '${TEMPLATE_NEW_VERSION}' [OK]\n"
      exit 0
    ;;
    -*|*)
      set +e
      case "${1}" in
        -a)
          help 'archive'
          exit 0
        ;;
        -c)
          help 'chroot'
          exit 0
        ;;
        -d)
          help 'download'
          exit 0
        ;;
        -g)
          help 'generate'
          exit 0
        ;;
        -i)
          help 'info'
          exit 0
        ;;
        -m)
          help 'mount'
          exit 0
        ;;
        -u)
          help 'update'
          exit 0
        ;;
      esac
    ;;
  esac
done
set +e
help 'help'
shift $((OPTIND-1))
