#!/bin/bash

## Copyright (C) 2012 - 2020 ENCRYPTED SUPPORT LP <adrelanos@riseup.net>
## See the file COPYING for copying conditions.

## The idea of this bash fragment is:
## Say nothing, if everything goes well, but dump everything on error.

## It allows to easily look inside the xtrace of a (Debian maintainer) script,
## when the DEBDEBUG environment variable is set to 1.

## To use it in other scripts, use something like this:
# if [ -f /usr/local/lib64/helper-scripts/pre.bsh ]; then
#    source /usr/local/lib64/helper-scripts/pre.bsh
# fi

## Error log:
## - implement trap ERR if function errorhandlergeneral does not exist
## - implements a simple error handler if non exists
## - run silent by default
## - write xtrace to temporary log
## - show full xtrace on unexpected non-zero exit code
## - show exit code on unexpected non-zero exit code
## - run syntax check "bash -n" on this script
## - run syntax check "bash -n" on the script that sourced this script

## DEBDEBUG:
##
## enable xtrace (-x) for maintainer script when DEBDEBUG environment
## variable is set to 1.
## For example:
##    sudo DEBDEBUG=1 dpkg -i /path/to/package.deb

## SKIP_SCRIPTS
##
## The SKIP_SCRIPTS environment variable to skip scripts by name
## For example:
##    sudo DEBDEBUG=1 SKIP_SCRIPTS=" security-misc.postinst " dpkg -i /path/to/package.deb
##
## another example:
##
##    export DEBDEBUG=1
##     export SKIP_SCRIPTS+=" security-misc.postinst "
##     sudo -E dpkg -i /path/to/package.deb

## Colorful output: provides color function

## Shell options: enables errtrace

## Configuration Folders
##
## For example if the name of the package is 'security-misc':
## - /etc/security-misc_maint.d/*.conf
## - /usr/local/etc/security-misc_maint.d/*.conf
##
## For example if the name of the script is 'panic-on-oops':
## - /etc/panic-on-oops_pre.d/*.conf
## - /usr/local/etc/panic-on-oops_pre.d/*.conf

## {{{ pre.bsh 1.0

## bash script fragment

colors() {
   if [ "$TERM" = "" ]; then
      return 0
   fi

   ## Thanks to:
   ## http://mywiki.wooledge.org/BashFAQ/037
   ## Variables for terminal requests.
   [[ -t 2 ]] && {
       alt=$(      tput smcup  || tput ti      ) # Start alt display
       ealt=$(     tput rmcup  || tput te      ) # End   alt display
       hide=$(     tput civis  || tput vi      ) # Hide cursor
       show=$(     tput cnorm  || tput ve      ) # Show cursor
       save=$(     tput sc                     ) # Save cursor
       load=$(     tput rc                     ) # Load cursor
       bold=$(     tput bold   || tput md      ) # Start bold
       stout=$(    tput smso   || tput so      ) # Start stand-out
       estout=$(   tput rmso   || tput se      ) # End stand-out
       under=$(    tput smul   || tput us      ) # Start underline
       eunder=$(   tput rmul   || tput ue      ) # End   underline
       reset=$(    tput sgr0   || tput me      ) # Reset cursor
       blink=$(    tput blink  || tput mb      ) # Start blinking
       italic=$(   tput sitm   || tput ZH      ) # Start italic
       eitalic=$(  tput ritm   || tput ZR      ) # End   italic
   [[ $TERM != *-m ]] && {
       red=$(      tput setaf 1|| tput AF 1    )
       green=$(    tput setaf 2|| tput AF 2    )
       yellow=$(   tput setaf 3|| tput AF 3    )
       blue=$(     tput setaf 4|| tput AF 4    )
       magenta=$(  tput setaf 5|| tput AF 5    )
       cyan=$(     tput setaf 6|| tput AF 6    )
   }
       white=$(    tput setaf 7|| tput AF 7    )
       default=$(  tput op                     )
       eed=$(      tput ed     || tput cd      )   # Erase to end of display
       eel=$(      tput el     || tput ce      )   # Erase to end of line
       ebl=$(      tput el1    || tput cb      )   # Erase to beginning of line
       ewl=$eel$ebl                                # Erase whole line
       draw=$(     tput -S <<< '   enacs
                                   smacs
                                   acsc
                                   rmacs' || { \
                   tput eA; tput as;
                   tput ac; tput ae;         } )   # Drawing characters
       back=$'\b'
   } 2>/dev/null ||:
}

source_config_folder() {
   ## dpkg sets environment variables
   ## example:
   ## DPKG_MAINTSCRIPT_PACKAGE=security-misc

   if [ "$DPKG_MAINTSCRIPT_PACKAGE" = "" ]; then
      pre_bsh_settings_folder="${own_filename}_pre.d"
   else
      pre_bsh_settings_folder="${DPKG_MAINTSCRIPT_PACKAGE}_maint.d"
   fi

   ## example:
   ## pre_bsh_settings_folder=security-misc_maint.d

   shopt -s nullglob
   local i

   ## example:
   ## /etc/panic-on-oops_pre.d/*.conf
   ## /usr/local/etc/panic-on-oops_pre.d/*.conf

   true "folder 1: /etc/${pre_bsh_settings_folder}/*.conf"
   true "folder 2: /usr/local/etc/${pre_bsh_settings_folder}/*.conf"

   for i in /etc/${pre_bsh_settings_folder}/*.conf /usr/local/etc/${pre_bsh_settings_folder}/*.conf; do
      bash_n_exit_code="0"
      bash_n_output="$(bash -n "$i" 2>&1)" || { bash_n_exit_code="$?" ; true; };
      if [ ! "$bash_n_exit_code" = "0" ]; then
         force_output "Invalid config file: $i
   bash_n_exit_code: $bash_n_exit_code
   bash_n_output:
   $bash_n_output" >&2
         rm -f "$TEMP_FILE_PRE_BSH"
         exit 1
      fi
      source "$i"
   done
   shopt -u nullglob
}

check_scripts_to_skip() {
   local skip_script
   for skip_script in $SKIP_SCRIPTS; do
      if [ "$skip_script" = "$own_filename" ]; then
         force_output "INFO: Skipping $own_filename, because SKIP_SCRIPTS includes it."
         rm -f "$TEMP_FILE_PRE_BSH"
         exit 0
      fi
   done
}

disable_echo() {
   if [ "$disabled_echo" = "true" ]; then
      return 0
   fi
   exec 5>&1 1>> "$TEMP_FILE_PRE_BSH"
   exec 6>&2 2>> "$TEMP_FILE_PRE_BSH"
   disabled_echo=true
}

enable_echo() {
   if [ "$disabled_echo" = "true" ]; then
      exec 1>&5
      exec 2>&6
      disabled_echo=false
   fi
}

force_output() {
   if [ "$disabled_echo" = "true" ]; then
      redisable_echo="true"
      enable_echo
   fi
   echo "$@"
   if [ "$redisable_echo" = "true" ]; then
      disable_echo
   fi
}

error_handler_pre() {
   local exit_code="$?"
   local last_err="$BASH_COMMAND"

   if [ ! "$DEBDEBUG" = "1" ]; then
      local output
      output="$(cat "$TEMP_FILE_PRE_BSH")"
   fi

   if [ "$output" = "" ]; then
      output="## See above."
   fi

   force_output "
####################################################################
## ${red}${bold}BEGIN ERROR in $0 detected!${reset}
##
## ${under}ERROR LOG${reset}:
$output
##
## ${under}BASH_COMMAND${reset}: $BASH_COMMAND
## ${under}EXIT_CODE${reset}: $exit_code
##
## ${red}${bold}END ERROR in $0 detected!${reset}
## ${red}${bold}Please report this bug!${reset}
####################################################################
" 1>&2

   rm -f "$TEMP_FILE_PRE_BSH"
   exit 1
}

## config-package-dev doesn't like 'set -o pipefail'
## http://mailman.mit.edu/pipermail/config-package-dev/2015-May/000041.html
#set -o pipefail

set -o errtrace

TEMP_FILE_PRE_BSH="$(mktemp)"

if test -o xtrace ; then
   true "INFO: Setting DEBDEBUG to 1, because xtrace (-x) is set."
   DEBDEBUG="1"
fi

if [ "$DEBDEBUG" = "1" ]; then
   set -x
fi

if [ "$disable_echo" = "true" ]; then
   disable_echo
fi

colors

## {{ Set up error handler.
if [ "$(type -t errorhandlergeneral)" = "function" ]; then
   ## Function errorhandlergeneral exists (declared in
   ## help-steps/pre). Prefer to use the more feature rich version of the error
   ## handler.
   trap "errorhandlergeneral" ERR
else
   ## Function errorhandlergeneral does not exist.

   ## Check if any trap is already declared.
   if [ "$(trap -p ERR)" = "" ]; then
      ## No trap exist yet.
      ## Fall back to a simpler error handler.
      trap "error_handler_pre" ERR
   fi
fi
## }}

## syntax check this script
bash -n "$BASH_SOURCE"

## syntax check script that sourced this script
bash -n "$0"

own_filename="${0##*/}"

source_config_folder

check_scripts_to_skip

## }}}
