# fiper-functions.sh
#
# Shell functions needed by fiper deploy scripts

# Turn on debugging if requested
if [ -n "$SH_DEBUG" ]; then
    set -x
fi

##########################################################################
### Define needed shell functions.
##########################################################################
#
# Conventions:

# There is a problem with shell - all variables exception positional arguments are global
# unless the 'typeset' or 'local' command is used (which is new with KSH).
# To avoid conflicts in shell functions, the following rules are observed:
# - Shell functions use only single-letter variables or a letter and a digit.  
# - Nothing outside the functions use single-letter variable names.
# - Environment variables are always in upper case.
# - Variables of the calling shell are lower-case and more than one letter
# A function should not call another function except for those that use NO local vars,
# like retString, fail, err, warn.

# Note: ALL file names and paths in these shell scripts use ';' as path separator
# and '/' as dectory separator on both Unix and Windows.
# function 'stdPath' converts local path and file to this form.
# function 'localPath' converts back to the local form.
# The alternative of using 'typeset -H' or 'typeset -h' was not used for portability.

# When code must 'cd' to a different dectory, the code is inside '( )' to force a sub-shell
# This avoids any issues with having to 'cd' back to where I started.

# Boolean shell variables use the empty string '' as false and '1' as true.
# This can be tested with [ "$bool" ] for true and [ ! "$bool" ] for false.
# Note that the double quotes are required.
# This is preferred to the alternative of setting the variable to 'true' or 'false' and
# testing with 'if $bool; then' only because the 'if $bool;' form cannot be easily negated.

# Useful shell functions
# The following 4 functions form a hierarchy of messages.
# Fail - fatal error.  Log to stderr and halt.
# warn - non-fatal error.  Log to stderr.
# note - progress message.  Log to stdout.
# msg  - debugging message.  Log to stdout only if -v passed on command line.

# fail - print a message to stderr and exit
#   Note use of "printf '%s'" instead of 'echo' to avoid backslash substitution,
#   since 'echo -E' is not standard and 'print -r' is only KSH (not bash).
fail() {
    printf '#### %s\n' "$*" >&2
    exit 1
}

# warn - print a message to stderr and continue
warn() {
    printf '## %s\n' "$*" >&2
}

# note - print a progress messsage
note() {
    printf '# %s\n' "$*"
}

# msg - print a message if VERBOSE is set.
msg() {
    if [ "$F4ALL_VERBOSE" ]; then
        printf '%s\n' "$*"
    fi
}

ERROR_SEEN=""

# err - print a message to stderr and record an error condition.
#   error condition is later checked with checkErr
err() {
    warn "$*"
    ERROR_SEEN=1
}

# checkErr - check if an error has been reported.  If one has, exit
checkErr() {
    if [ "$ERROR_SEEN" ]; then
        fail "Exiting due to errors."
    fi
}

# hadErr - check if an error occurred, and reset the error flag.
#   Use this instead of checkErr if you want to recover from an error.
#   Standard usage is:   'if [ "$(hadErr)" ]; then'
hadErr() {
    if [ "$ERROR_SEEN" ]; then
        ERROR_SEEN=""
        ret=1
    else
        ret=''
    fi
    return $ret
}

# retString - returns the value of a String variable safely even if it contains backslashes.
#  Note that 'echo $1' would substitute backslash escapes, messing up C:\bar.
retString() { printf '%s\n' "$1" ; }


# Determine if this script is running on Unix or Windows.
# Define useful variables and functions specific to platforms:
# Variables:
#    ON_WINDOWS - true if on Windows, false if on Unix
#    SCRIPT - extension for a script file on this platform.  '.bat' on Windows, '.sh' on Unix.
#   CSCRIPT - extension for Weblogic scripts:  '.cmd' on Windows, '.sh' on Unix.
#   CMD - Extension for executable file on this platform.  '.exe' on Windows, empty on Unix.
#    NUL - Null device for this platform, 'NUL:' on Windows, '/dev/null' on Unix.
# Functions:
#    stdPath - convert path to standard format with '/' dectory separator and ';' for path sep.
#    localPath - convert path to local form.
if [ $(uname -s) = "Windows_NT" ]; then
    ON_WINDOWS=1
    SCRIPT=.bat
    XSCRIPT=.bat
    CSCRIPT=.cmd
    CMD=.exe
    NUL=NUL:
    # On Windows convert to/from backslashes
    stdPath() { printf '%s\n' "${1//\\/\/}" ; }
    localPath() { printf '%s\n' "${1//\//\\}" ; }

    # Insure MKS toolkit is at front of path for find, sort
    PATH="$(dirname $(which egrep));$PATH"
else
    ON_WINDOWS=''
    SCRIPT=.sh
    XSCRIPT=''
    CSCRIPT=.sh
    CMD=
    NUL=/dev/null
    # On Unix convert ":" to/from ";"
    # No need to worry about escapes - no backslashes used
    stdPath() { echo "${1//:/;}" ; }
    localPath() { echo "${1//;/:}" ;}

    # Force default cd, cat, pwd to front of path
    # Otherwise 'cat' can be picked up from the CATIA RTV.
    PATH="/bin:/usr/bin:$PATH"
fi

# makeAbsolute - convert a file path into an absolute path.
# usage: makeAbsolute PATH
makeAbsolute() {
    # Force forward slashes everywhere
    n=${1//\\/\/}
    case "$n" in
        '' )
            # Empty name.  Leave it empty
            ;;
        /* | ?:/* )
            # already absolute.  Leave unchanged
            ;;
        *)
            # Relative.  Add PWD to front
            n="$PWD/$n"
            ;;
    esac
    retString "$n"
}

# cannonPath - convert a path to an directory to the cannonical form.
cannonPath() {
    if [ -d "$1" ]; then
        retString "$(cd "$1"; \pwd)"
    else
        localPath "$1"
    fi
}

# extendPath - Expand a path by adding each entry from a list of names to each entry of the path.
#  Usage: extendPath PATH NAMELIST
# Both PATH and NAMELIST must use ';' as path separator.
# This is used to extend things like CATInstallPath to select a group of subdectories of
# each element of the concatenation path.
extendPath() {
    p=$1
    m=$2
    IFS=';'
    r=''
    for p2 in $p; do
        for m2 in $m; do
            r="$r;$p2/$m2"
        done
    done
    unset IFS
    # Remove leading ';' when returning
    retString "${r#;}"
}


# findFile - find a single file or dectory in a path
#  Usage: findFile FILE PATH
# Reports an error and returns '--NOTFOUND--' if file cannot be found.
findFile() {
    f=$(stdPath "$1")
    p=$(stdPath "$2")
    IFS=';'
    for p2 in $p; do
        r="$p2/$f"
        [ -e "$r" ] && break;
    done
    if [ ! -e "$r" ]; then
        err "Unable to find file $f in path $p"
        r="--NOTFOUND--/$f"
    fi
    unset IFS
    retString "$r"
}

# findPath - look up a series of names in a Path and return the
#   path made up of all the absolute file paths.
# Usage: findPath NAMES PATH
#   NAMES - series of relative file names separated by ';'
findPath() {
    n=$(stdPath "$1")
    p=$(stdPath "$2")
    IFS=';'
    r2=''
    for f in $n; do
        for p2 in $p; do
            r="$p2/$f"
            [ -e "$r" ] && break;
        done
        if [ ! -e "$r" ]; then
            err "Unable to find file $f in path $p"
            r="--NOTFOUND--/$f"
        fi
        r2="$r2;$r"
    done
    unset IFS
    retString "${r2#;}"
}

# checkVar - Check for a required environment variable being set
# Usage: checkVar VARNAME [ FILE ]
#   VARNAME - variable to be checked for non-empty value.
#   FILE - (optional) if passed, check that $VARNAME/$FILE exists.
checkVar() {
    v="$1"
    f="$2"
    if [ -z "$v" ]; then
        fail "checkVar called without environment variable."
    fi
    eval 'v2=${'$(echo $v)'}'
    if [ -z "$v2" ]; then
        err "Required Variable $v is not set."
    elif [ -n "$f" -a ! -e "$v2/$f" ]; then
        err "Environment variable $v='$v2' does not point to file $f"
    fi
    msg "parameter $v = $v2"
}

# defaultVar - Set a variable to a default value if it isn't already set.
#      Optionally check if the variable points to a specific file
# Usage: defaultVar VARNAME DEFAULT [ FILE ]
#      VARNAME - name of the shell variable to check.
#      DEFAULT - default value to assign to the variable if it isn't set yet.
#      FILE - (optional) check if $VARNAME/$FILE exists
defaultVar() {
    v="$1"
    d="$2"
    f="$3"
    if [ -z "$v" ]; then
        fail "checkVar called without environment variable."
    fi
    # Get current value
    eval 'v2=${'$(echo $v)'}'

    # If not set, set to default.  Retrieve value to insure assignment is good.
    if [ -z "$v2" ]; then
        eval "$v='${d}'"
        eval 'v2=${'$(echo $v)'}'
    fi
    if [ -n "$f" -a ! -e "$v2/$f" ]; then
        err "Environment variable $v does not point to file $f"
    fi
    msg "parameter $v = $v2"
}

# upcase - convert the argument string to upper case.
upcase() {
    printf '%s\n' "$1" | tr a-z A-Z
}

# isAdmin - is this user a local Administrator.
# Only effect is to set the return code:  success = is admin; fail = not admin or can't tell.
isAdmin() {
    retcode=1
    if [ "$ON_WINDOWS" ]; then
        # Are administrator if can see the contents of the Administrator dectory.
        homed=$(cannonPath "$USERPROFILE/..")
        admind="$homed/Administrator"
        if [ ! -d "$admind" ]; then
            admind="$homed/ntadmin"
        fi
        if [ ! -d "$admind" ]; then
            : # cannot determine what is going on.  Punt and return false
        else
            a=$( ls "$admind" | wc -l )
            if [ "$a" -gt 1 ]; then
                retcode=0
            fi
        fi
    else
        # On Unix.  Trivial check for user ID 0.
        if [ $(id -u) = 0 ]; then
            retcode=0
        fi
    fi
    return $retcode
}

# addpath - add an entry to the PATH variable if it isn't already there.
# Usage: addpath NEWDIR
#   NEWDIR - directory to add.  Must exit
addpath() {
  d=$(stdPath "$1")
  p=$(stdPath "$PATH")
  if [ ! -d "$d" ]; then return; fi
  expr ";$p;" : ".*;$d;.*" >$NUL
  if [ $? -ne 0 ]; then
    export PATH=$(localPath "$p;$d")
  fi
}

# cmd - run a command with optional logging of the command executed, and output directed
#  to file $LOG_DIR/cmd.log or $2
# Usage: cmd "COMMAND" [ LOGFILE ]
#    COMMAND - command to execute as ONE argument.  Put single quotes inside double
#              quotes to quote parts of the command.
#    LOGFILE - file to log command output and error to.
#  Returns: exit code of commmand.
cmd() {
    c="$1"
    l="$2"
    if [ ! "$l" ]; then
        l="$LOG_DIR/cmd.log"
    fi
    if [ "$F4ALL_VERBOSE" ]; then
        printf 'Cmd: %s\n' "$c"
    fi
    eval "$c" >"$l" 2>&1
    r=$?
    if [ "$F4ALL_VERBOSE" ]; then
        cat "$l"
    fi
    return $r
}
