#!/bin/bash
# ###########################
# Bash Shell Function Library
# ###########################
#
# Author: Louwrentius <louwrentius@gmail.com>
# Contributions by: Jani Hurskainen
#
# Copyright © 2010
#
# Released under the curren GPL version.
#
# Description:
#
# This is a shell script library. It contains functions that can be called by
# programs that include (source) this library. 
#
# By simply sourcing this library, you can use all available functions as 
# documented on the projects page.
#
#

BSFL_VERSION="2.00-beta-2"

#
# Do not edit this file. Just source it into your script
# and override the variables to change their value.
#

init () {

    #
    # Debug mode shows more verbose output to screen and log files.
    # Value: yes or no (y / n)
    #
    DEBUG=no
    
    # 
    # Syslog style log messages
    #
    if ! defined LOGDATEFORMAT
    then
        LOGDATEFORMAT="%b %e %H:%M:%S"
    fi
    if ! defined LOG_FILE
    then
        # LOG_FILE=$0.log
        LOG_FILE=/tmp/makesite.log
    fi
    
    #
    # Enable / disable logging to a file
    # Value: yes or no (y / n)
    #
    if ! defined LOG_ENABLED
    then
        # LOG_ENABLED=no
        LOG_ENABLED=yes
    fi
    if ! defined SYSLOG_ENABLED
    then
        SYSLOG_ENABLED=no
    fi
    if ! defined SYSLOG_TAG
    then
        SYSLOG_TAG=$0
    fi
    
    #
    # Use colours in output.
    #
    RED="tput -T xterm setaf 1"
    GREEN="tput -T xterm setaf 2"
    YELLOW="tput -T xterm setaf 3"
    BLUE="tput -T xterm setaf 4"
    MAGENTA="tput -T xterm setaf 5"
    CYAN="tput -T xterm setaf 6"
    LIGHT_BLUE="$CYAN"
    BOLD="tput -T xterm bold"
    DEFAULT="tput -T xterm sgr0"
    
    RED_BG="tput -T xterm setab 1"
    GREEN_BG="tput -T xterm setab 2"
    YELLOW_BG="tput -T xterm setab 3"
    BLUE_BG="tput -T xterm setab 4"
    MAGENTA_BG="tput -T xterm setab 5"
    CYAN_BG="tput -T xterm setab 6"
    
    # 
    # Bug fix for Bash, parsing exclamation mark.
    #
    set +o histexpand
    #
    # returns 0 if a variable is defined (set)
    # returns 1 if a variable is unset
    #

}

function defined {
    [[ ${!1-X} == ${!1-Y} ]]
}

#
# returns 0 if a variable is defined (set) and value's length > 0
# returns 1 otherwise
#
function has_value {
    if defined $1; then
        if [[ -n ${!1} ]]; then
            return 0
        fi
    fi
    return 1
}

#
# returns 0 if a directory exists
# returns 1 otherwise
#
function directory_exists {
    if [[ -d "$1" ]]; then
        return 0
    fi
    return 1
}

#
# returns 0 if a (regular) file exists
# returns 1 otherwise
#
function file_exists {
    if [[ -f "$1" ]]; then
        return 0
    fi
    return 1
}

#
# returns lowercase string
#
function tolower {
    echo "$1" | tr '[:upper:]' '[:lower:]'
}

#
# returns uppercase string
#
function toupper {
    echo "$1" | tr '[:lower:]' '[:upper:]'
}

#
# Only returns the first part of a string, delimited by tabs or spaces
#
function trim {
    echo $1
}

#
# Dummy function to provide usage instructions.
# Override this function if required.
#
show_usage () {
   
    MESSAGE="$1" 
    echo "$MESSAGE"
    exit 1
}

#
# Checks if a variable is set to "y" or "yes".
# Usefull for detecting if a configurable option is set or not.
#
option_enabled () {

    VAR="$1"
    VAR_VALUE=$(eval echo \$$VAR)
    if [[ "$VAR_VALUE" == "y" ]] || [[ "$VAR_VALUE" == "yes" ]]
    then
        return 0
    else
        return 1
    fi
}

#
# The log funcion just puts a string into a file, prepended with a date & time in 
# syslog format.
#

log2syslog () {

    if option_enabled  SYSLOG_ENABLED
    then
       MESSAGE="$1"
       logger -t "$SYSLOG_TAG" " $MESSAGE" #The space is not a typo!"
    fi
}

#
# This function writes messages to a log file and/or syslog
# The only argument is a message that has to be logged.
# 

log () {
    
    if option_enabled LOG_ENABLED || option_enabled SYSLOG_ENABLED 
    then
        LOG_MESSAGE="$1" 
        DATE=`date +"$LOGDATEFORMAT"`

        if has_value LOG_MESSAGE
        then
            LOG_STRING="$DATE $LOG_MESSAGE"
        else
            LOG_STRING="$DATE -- empty log message, no input received --"
        fi

        if option_enabled LOG_ENABLED
        then
            echo "$LOG_STRING" >> "$LOG_FILE"
        fi

        if option_enabled SYSLOG_ENABLED
        then
            #
            # Syslog already prepends a date/time stamp so only the message 
            # is logged. 
            #
            log2syslog "$LOG_MESSAGE"
        fi
    fi
}


#
# This function basically replaces the 'echo' function in bash scripts.
# The added functionality over echo is logging and using colors. 
#
# The first argument is the string / message that must be displayed.
# The second argument is the text color.

msg () {

    MESSAGE="$1"
    COLOR="$2"

    if ! has_value COLOR
    then
        COLOR="$DEFAULT"
    fi

    if has_value "MESSAGE"
    then
        $COLOR
        echo "$MESSAGE" 
        $DEFAULT
    else
        echo "-- no message received --"
    fi
}

#
# This function echos a message 
# and displays the status at the end of the line.
#
# It can be used to create status messages other
# than the default messages available such as
# OK or FAIL
#
msg_status () {

    MESSAGE="$1"
    STATUS="$2"

    msg "$MESSAGE"
    display_status "$STATUS"
}

#
# These functions are just short hand for messages like
# msg_status "this message is ok" OK
#

#
# The following functions are shorthand for 
# msg_status "a message" OK 
# msg_status "another message" FAIL


msg_emergency () {

    MESSAGE="$1"
    STATUS="EMERGENCY"
    msg_status "$MESSAGE" "$STATUS"
}

msg_alert () {

    MESSAGE="$1"
    STATUS="ALERT"
    msg_status "$MESSAGE" "$STATUS"
}

msg_critical () {

    MESSAGE="$1"
    STATUS="CRITICAL"
    msg_status "$MESSAGE" "$STATUS"
}

msg_error () {

    MESSAGE="$1"
    STATUS="ERROR"
    msg_status "$MESSAGE" "$STATUS"
}

msg_warning () {

    MESSAGE="$1"
    STATUS="WARNING"
    msg_status "$MESSAGE" "$STATUS"
}

msg_notice () {
    MESSAGE="$1"
    STATUS="NOTICE"
    msg_status "$MESSAGE" "$STATUS"
}

msg_info () {
    MESSAGE="$1"
    STATUS="INFO"
    msg_status "$MESSAGE" "$STATUS"
}

msg_debug () {
    MESSAGE="$1"
    STATUS="DEBUG"
    msg_status "$MESSAGE" "$STATUS"
}

msg_ok () {

    MESSAGE="$1"
    STATUS="OK"
    msg_status "$MESSAGE" "$STATUS"
}

msg_not_ok () {

    MESSAGE="$1"
    STATUS="NOT_OK"
    msg_status "$MESSAGE" "$STATUS"
}

msg_fail () {

    MESSAGE="$1"
    STATUS="FAILED"
    msg_status "$MESSAGE" "$STATUS"
}

msg_success () {
    MESSAGE="$1"
    STATUS="SUCCESS"
    msg_status "$MESSAGE" "$STATUS"
}

msg_passed () {
    MESSAGE="$1"
    STATUS="PASSED"
    msg_status "$MESSAGE" "$STATUS"
}

check_status () {

    CMD="$1"
    STATUS="$2"

    if [ "$STATUS" == "0" ]
    then
        msg_ok "$CMD"
    else
        msg_fail "$CMD"
    fi
}

#
# Private function
#
# This is a function that just positions
# the cursor one row up and to the right.
# It then prints a message with specified
# Color
# It is used for displaying colored status messages on the
# Right side of the screen.
#
# ARG1 = "status message (OK / FAIL)"
# ARG2 = The color in which the status is displayed.
#
raw_status () {

    STATUS="$1"
    COLOR="$2"

    function position_cursor () {

        let RES_COL=`tput -T xterm cols`-12
        tput -T xterm cuf $RES_COL
        tput -T xterm cuu1
    }

    position_cursor
    echo -n "["
    $DEFAULT
    $BOLD
    $COLOR
    echo -n "$STATUS"
    $DEFAULT
    echo "]"
}

#
# This function converts a status message to a particular color.
#
display_status () {


    STATUS="$1"

    case $STATUS in 

    EMERGENCY )
            STATUS="EMERGENCY"
            COLOR="$RED"
            ;;
    ALERT )
            STATUS="  ALERT  "
            COLOR="$RED"
            ;;
    CRITICAL )
            STATUS="CRITICAL "
            COLOR="$RED"
            ;;
    ERROR )
            STATUS="  ERROR  " 
            COLOR="$RED"
            ;;

    WARNING )
            STATUS=" WARNING "  
            COLOR="$YELLOW"
            ;;

    NOTICE )
            STATUS=" NOTICE  "  
            COLOR="$BLUE"
            ;;
    INFO )
            STATUS="  INFO   "  
            COLOR="$LIGHT_BLUE"
            ;;
    DEBUG )
            STATUS="  DEBUG  "
            COLOR="$DEFAULT"
            ;;    

    OK  ) 
            STATUS="   OK    "  
            COLOR="$GREEN"
            ;;
    NOT_OK)
            STATUS=" NOT OK  "
            COLOR="$RED"
            ;;

    PASSED ) 
            STATUS=" PASSED  "  
            COLOR="$GREEN"
            ;;

    SUCCESS ) 
            STATUS=" SUCCESS "  
            COLOR="$GREEN"
            ;;
    
    FAILURE | FAILED )
            STATUS=" FAILED  "  
            COLOR="$RED"
            ;;

    *)
            STATUS="UNDEFINED"
            COLOR="$YELLOW"
    esac

    raw_status "$STATUS" "$COLOR"
}

#
# Exit with error status 
#
bail () {

    ERROR="$?"
    MSG="$1"
    if [ ! "$ERROR" = "0" ]
    then
        msg_fail "$MSG"
        exit "$ERROR"
    fi
}

#
# This function executes a command provided as a parameter
# The function then displays if the command succeeded or not.
#
cmd () {

    COMMAND="$1"
    msg "Exc: ${COMMAND:0:59}..."

    RESULT=`$COMMAND 2>&1`
    ERROR="$?"

    MSG="Cmd: ${COMMAND:0:59}..."
    
    tput -T xterm cuu1

    if [ "$ERROR" == "0" ]
    then
        msg_ok "$MSG"
        log "$COMMAND"
        if [ "$DEBUG" == "1" ]
        then
            msg "$RESULT"
        fi
    else
        msg_fail "$MSG"
        log "$RESULT"
        echo "$RESULT"
    fi

    return "$ERROR"
}

#
# These functions can be used for timing how long (a) command(s) take to
# execute.
#
now () {

   echo $(date +%s)
}

elapsed () {
    
    START="$1"
    STOP="$2"

    echo $(( STOP - START ))
}

#
# Prints an error message ($2) to stderr and exits with the return code ($1).
# The message is also logged.
#
function die {
    local -r err_code="$1"
    local -r err_msg="$2"
    local -r err_caller="${3:-$(caller 0)}"

    msg_fail "ERROR: $err_msg"
    msg_fail "ERROR: At line $err_caller"
    msg_fail "ERROR: Error code = $err_code"
    exit "$err_code"
} >&2 # function writes to stderr

#
# Check if a return code ($1) indicates an error (i.e. >0) and prints an error
# message ($2) to stderr and exits with the return code ($1).
# The error is also logged.
#
# Die if error code is false.
#
function die_if_false {
    local -r err_code=$1
    local -r err_msg=$2
    local -r err_caller=$(caller 0)

    if [[ "$err_code" != "0" ]]
    then
        die $err_code "$err_msg" "$err_caller"
    fi
} >&2 # function writes to stderr

#
# Dies when error code is true
#
function die_if_true {
    local -r err_code=$1
    local -r err_msg=$2
    local -r err_caller=$(caller 0)

    if [[ "$err_code" == "0" ]]
    then
        die $err_code "$err_msg" "$err_caller"
    fi
} >&2 # function writes to stderr

#
# Replace some text inside a string.
#
function str_replace () {
    local ORIG="$1"
    local DEST="$2"
    local DATA="$3"

    echo "$DATA" | sed "s/$ORIG/$DEST/g"
}

#
# Replace string of text in file.
# Uses the ed editor to replace the string.
#
# arg1 = string to be matched
# arg2 = new string that replaces matched string
# arg3 = file to operate on.
#
function str_replace_in_file () {
    local ORIG="$1"
    local DEST="$2"
    local FILE="$3"

    has_value FILE 
    die_if_false $? "Empty argument 'file'"
    file_exists "$FILE"
    die_if_false $? "File does not exist"

    printf ",s/$ORIG/$DEST/g\nw\nQ" | ed -s "$FILE" > /dev/null 2>&1
    return "$?"
}

init


# Other stuff
check_program () {
    program=$1
    which $program 1>/dev/null || {
        die 127 "Command '$program' not found!" "$(caller 0)"
    }
}

info () {
    msg $1 $LIGHT_BLUE
}

cmd_or_die () {
    cmd "$1" || exit 1
}
