#!/bin/sh

#------------------------------------------------------------------------
# ${SELF} (${SELF_VERSION}) - Display a message on standard output
# Copyright © 2014 Pôle de compétences EOLE <eole@ac-dijon.fr>
#
# License CeCILL:
#  * in french: http://www.cecill.info/licences/Licence_CeCILL_V2-fr.html
#  * in english http://www.cecill.info/licences/Licence_CeCILL_V2-en.html

#------------------------------------------------------------------------
# Changes:
#    0.0.4  Add log utilities with a dedicated developper documentation
#    0.0.3  Add “--sources” option to write “${SELF}” code on standard output
#    0.0.2  Take care of DEBUG=yes environment variable
#    0.0.1  Initial release

#------------------------------------------------------------------------
# Usage: ${SELF} [--message <TEXT> | <OPTION>]
#
# Display a message on standard output.
#
# Options:
# --------
#
#     -m, --message <TEXT>     Display <TEXT> on standard output.
#                              Default: “${MESSAGE}"
#     -d, --debug              Enable debug messages
#     -h, --help               Show this message
#     -v, --version            Display version and copyright information
#     -c, --copyright          Display copyright information
#     -l, --licence            Display licence information
#         --changes            Display ChangeLog information
#     -s, --sources            Output software sources on standard output.
#
# Mandatory dependencies:
# -----------------------
# * “sh” like shell
# * “echo” with “-e” option
# * perl
#
# Optional dependencies:
# ----------------------
# None
#
# Bugs:
# ----
# Report bug to Équipe EOLE <eole@ac-dijon.fr>
# bugtracker: http://dev-eole.ac-dijon.fr/projects/<EOLE-SKELETOR>/issues

#------------------------------------------------------------------------
# Debug, first thing to do if something goes wrong, even in utilities
#
# “${DEBUG}” can be:
# - “all” to set “-x” option of the shell
# - “true” to enable the “debug()” function, set by “--debug” option
set -e

if [ "${DEBUG}" = 'all' ]
then
    set -x
fi

#------------------------------------------------------------------------
# Utilities for developpers:
#
# log ($@): write all parameters on standard output and call “flog()” to
#           write them in a file named “${LOG_FILE}” if it's:
#           - defined
#           - not a symlink
#           - a file or a named pipe or a socket
#           - writable or its parent directory is writable to create
#             a regular file.
#
# warn($@): call “log()” with all parameters, output of log() is
#           redirected to standard error.
#
# die($@): call “warn()” with all parameters, exit with code stored in
#          “${EXIT_CODE}” or “1” if it does not exit.
#
# debug($@): call “warn()” with all parameters if “${DEBUG}" is
#            “true”, the message is prefixed by the script name stored
#            in “${SELF}”
#
# flog ($@): write all parameters prefixed by current date and time
#            in a file named “${LOG_FILE}" if the variable is not empty.
#            The caller is responsible of the writable check of “${LOG_FILE}”.

# Take care of “-e” option to echo
type shopt > /dev/null && ECHO=echo || ECHO=/bin/echo

## Logger functions
# Check if “log()” could write to “${LOG_FILE}”
log_writable() {
    # First: check that filename is defined and not a symlink
    # Second: check that filename is a file, a named pipe or a socket
    # Thirt: if filename is writable or if its parent directory is writable
    [ -n "${1}" -a ! -L "${1}" ] \
        && [ -f "${1}" -o -p "${1}" -o -S "${1}" ] \
        && [ -w "${1}" -o -w "$(dirname ${1})" ]
}

flog() { [ -z "${LOG_FILE}" ] || ${ECHO} -e "$(date "+%Y-%m-%d %H:%M:%S"): $@" >> "${LOG_FILE}"; }
log() { ${ECHO} -e "$@"; log_writable "${LOG_FILE}" && flog "$@" || true; }
warn() { log "$@" >&2; }
debug() { [ "${DEBUG}" = all -o "${DEBUG}" = true ] && warn "${SELF}: $@" || true; }
die() { warn "$@"; exit ${EXIT_CODE:-1}; }

## Common option functions
# Display list of changes
changes(){
    ${ECHO} -e "${SELF}\n"
    perl -lne "s<\\$\\{([^\\}]+)\\}><\$ENV{\$1}>gxms;
        print substr(\$_, 2) if (/^# Changes/ .. /^\$/) =~ /^\\d+\$/" < "${0}"
}

# Set SELF_VERSION variable
self_version() {
    [ -z "${SELF_VERSION}" ] || return
    export SELF_VERSION=$(changes 2>&1 \
        | perl -lane 'if (m/^\s+\d+(?:\.\d+)*/) {print $F[0]; exit}')
}

# Display usage
usage() {
    self_version
    perl -lne "s<\\$\\{([^\\}]+)\\}><\$ENV{\$1}>gxms;
        print substr(\$_, 2) if (/^# Usage/ .. /^\$/) =~ /^\\d+\$/" < "${0}"
}

# Display licence
licence() {
    self_version
    perl -lne "s<\\$\\{([^\\}]+)\\}><\$ENV{\$1}>gxms;
        print substr(\$_, 2) if (/^# ${SELF} \(${SELF_VERSION}\)/ .. /^\$/) =~ /^\\d+\$/" < "${0}"
}

# Display sources making the software AGPL-3 ready
sources() {
    cat < "${0}"
}

#------------------------------------------------------------------------
# Global variables:
#
# Use “export” to make them available to subprocesses
#
# Empty log file by default
LOG_FILE=

# Used by common options functions, do not unexport or they fail
export SELF=$(basename $(readlink -e "${0}"))
export SELF_VERSION= # Set by function, here for reference

# Program specific variables, export to use it in “usage()”
export MESSAGE="Hello World!"

#------------------------------------------------------------------------
# Options
TEMP=$(getopt -o m:dhvcls --long message:,debug,help,version,copyright,licence,changes,sources -- "$@")

test $? = 0 || exit 1
eval set -- "${TEMP}"

while true
do
    case "${1}" in
        # Default options for utilities
        -h|--help)
            usage
            exit 0
            ;;
        -v|--version)
            licence | head -n 2
            exit 0
            ;;
        -c|--copyright)
            licence | tail -n +2
            exit 0
            ;;
        -l|--licence)
            licence
            exit 0
            ;;
        --changes)
            changes
            exit 0
            ;;
        -s|--sources)
            sources
            exit 0
            ;;

        -d|--debug)
            DEBUG=true
            shift
            ;;

        # Program options
        -m|--message)
            [ -n "${2}" ] || die "Message must not be empty"
            export MESSAGE="${2}"
            shift 2
            ;;

        # End of options
        --)
            shift
            break
            ;;
        *)
            die "Error: unknown argument '${1}'"
            ;;
    esac
done

#------------------------------------------------------------------------
# Start program

debug "This is a debug message on standard error"
${ECHO} "${MESSAGE}"