#!/bin/bash

help () {
    {
    echo "Usage: iocsh [options] [files] [macro=value] ..."
    echo "Start an EPICS iocsh and load files"
    echo
    echo "Options:"
    echo " -?, -h, --help  Show this page and exit."
    echo " -v, --version   Show version and exit."
    echo " -32             Force 32 bit version (on 64 bit systems)."
    echo " -x[.z[.y]]      Select EPICS base version x.z.y (e.g. 3.14.8, 3.15, 7)."
    echo " -d, --debug     Run IOC with gdb."
    echo " -dv             Run IOC with valgrind."
    echo " -dp             Run IOC with perf record."
    echo " -c 'cmd args'   Ioc shell command."
    echo " -s 'prog m=v'   Sequencer program (and arguments), run with 'seq'."
    echo "                 This forces an 'iocInit' before running the program."
    echo " -r module[,ver] Modue (optionally with version) loaded via 'require'."
    echo " -n name         Name of the IOC, used for prompt and \${IOC} variable."
    echo "                 Default: dirname if parent dir is \"ioc\" otherwise hostname."
    echo " @file           More arguments are read from file."
    echo
    echo "Supported filetypes:"
    echo " *.db, *.dbt, *.template  loaded via 'dbLoadRecords'"
    echo " *.subs, *.subst          loaded via 'dbLoadTemplate'"
    echo " *.dbd                    loaded via 'dbLoadDatabase'"
    echo " *.so                     loaded via 'dlload' (or 'ld' before 3.14.12)"
    echo "All other files are executed as startup scripts by the EPICS shell."
    echo "After a file you can specify substitutions like m1=v1 m2=v1 for that file."
    echo
    echo "Examples:"
    echo "  iocsh st.cmd"
    echo "  iocsh my_database.template P=XY M=3"
    echo "  iocsh -r my_module,version -c 'initModule()'"
    echo "  iocsh -3.15.4 -dp st.cmd"
    echo "  iocsh -c 'var requireDebug 1' st.cmd"
    } >&2
    exit
}

version () {
    {
    echo "iocsh by Dirk Zimoch"
    } >&2
    exit
}

# realpath and readlink are not available on all systems, let's try what works...
rp() {
  ( realpath $1 || readlink -f $1 || readlink $1 || (cd -P $1 && echo $PWD) || (x=$(\ls -ld $1) && echo ${x##* }) || echo $1 ) 2>/dev/null
}    

# if EPICS_HOST_ARCH is not set guess it
if [ -z "$EPICS_HOST_ARCH" ]
then
    EPICS_HOST_ARCH=$(basename $(dirname $(rp $(which caRepeater))))
    if [ -n "$EPICS_HOST_ARCH" ]
    then
        echo "Guessing EPICS_HOST_ARCH=$EPICS_HOST_ARCH" >&2
    else
        echo "EPICS_HOST_ARCH is not set" >&2
        exit 1
    fi
fi

while true
do
    case $1 in
        ( -32 )
            EPICS_HOST_ARCH=${EPICS_HOST_ARCH%_64}
        ;;
        ( -[1-9]* )
            unset EPICS_BASE
            BASE=${1#-}
        ;;
        ( * ) break
        ;;
    esac
    shift
done

# Either EPICS or EPICS_BASE should be set to the install directory
if [ -z "$EPICS_BASE" ]
then
    if [ -z "$EPICS" ]
    then
        # look for some standard install directories
        for EPICS in /usr/local/epics /opt/epics /epics
        do
            if [ -d $EPICS ]
            then
                break
            fi
        done
        if [ ! -d "$EPICS" ]
        then
            EPICS=$(dirname $(dirname $(dirname $(dirname $(ldd $(which caRepeater) | awk '/libca/ {print $3}')))))
            echo "Guessing EPICS=$EPICS"
        fi
        if [ ! -d "$EPICS" ]
        then
            echo "Cannot find EPICS installation directory." >&2
            echo "Try setting EPICS environment variable." >&2
            exit 1
        fi
    fi
    if [ -z "$BASE" ]
    then
        EPICS_BASE=$(\ls -1vrd $EPICS/base/bin/{${EPICS_HOST_ARCH},${EPICS_HOST_ARCH%_64}} 2>/dev/null | head -n1)
    else
        # find highest (requested) EPICS version that supports our architecture (or its 32 bit version)
        EPICS_BASE=$(\ls -1vrd $EPICS/base-$BASE*/bin/{${EPICS_HOST_ARCH},${EPICS_HOST_ARCH%_64}} 2>/dev/null | head -n1)
    fi
    if [ -z $EPICS_BASE ]
    then
        if [ -z "$(\ls -1vrd $EPICS/base-$BASE*/ 2>/dev/null)" ]
        then
            echo "No EPICS $BASE installed." >&2
            exit 1
        fi
        echo EPICS $BASE not available for EPICS_HOST_ARCH=$EPICS_HOST_ARCH. >&2
        exit 1
    fi
    # maybe we need to change from 64 bit to 32 bit
    if [ $EPICS_HOST_ARCH != ${EPICS_BASE#*/bin/} ]
    then
        EPICS_HOST_ARCH=${EPICS_BASE#*/bin/}
        echo "No 64 bit version in ${EPICS_BASE%bin*}." >&2
        echo "Switching to 32 bit version $EPICS_HOST_ARCH." >&2
    fi
    EPICS_BASE=$(rp ${EPICS_BASE%bin*})
fi
if [ ! -d $EPICS_BASE ]
then
    echo "Cannot find EPICS_BASE directory." >&2
    echo "Try setting EPICS_BASE environment variable to full path" >&2
    exit 1
fi

# Get actual EPICS revision
if [ -f $EPICS_BASE/configure/CONFIG_BASE_VERSION ]
then
    eval $(awk -F '[ \t]*=[ \t]*' '
        /^[ \t]*EPICS_VERSION[ \t]*=/ {v=$2}
        /^[ \t]*EPICS_REVISION[ \t]*=/ {r=$2}
        /^[ \t]*EPICS_MODIFICATION[ \t]*=/ {m=$2+0}
        END {print "BASE="v"."r"."m";BASECODE="v*10000+r*100+m}
    ' < $EPICS_BASE/configure/CONFIG_BASE_VERSION)
elif [ -f $EPICS_BASE/lib/$EPICS_HOST_ARCH/libCom.so ]
then
    eval $(strings $EPICS_BASE/lib/$EPICS_HOST_ARCH/libCom.so | awk -F'[.R-]' '
        /EPICS R[0-9]/ {print "BASE="$2"."$3"."$4";BASECODE="$2*10000+$3*100+$4 }')
else
    echo "Cannot guess EPICS base version." >&2
    exit 1;
fi

# IOC name derives from hostname
# (trailing possible '\r' under cygwin)
IOC=$(hostname|tr -d '\r')
# trailing possible domain name
IOC=${IOC%%.*}
# or get IOC name from start directory following PSI convention
if [ $(basename $(dirname $PWD)) = "ioc" ]
then
    IOC=$(basename $PWD)
fi
export IOC

# Check for 64 bit versions, default to 32 bit
if [ ! -d $EPICS_BASE/lib/${EPICS_HOST_ARCH} -a -d $EPICS_BASE/lib/${EPICS_HOST_ARCH%_64} ]
then
    echo "No 64 bit EPICS installation found. Defaulting to 32 bit" >&2
    EPICS_HOST_ARCH=${EPICS_HOST_ARCH%_64}
fi
export EPICS_HOST_ARCH

# setup search path for require
ODIR=O.${BASE}_$EPICS_HOST_ARCH
EPICS_DRIVER_PATH=.:bin/$EPICS_HOST_ARCH:bin:snl:../snl:$ODIR:src/$ODIR:snl/$ODIR:../snl/$ODIR:${EPICS_DRIVER_PATH#:}

#Special PSI: find installation base for libs from working directory
D=$(rp $PWD)
I=${D%/iocBoot/*}
if [ $I != $D ]
then
    INSTBASE=$I
fi

EPICS_DRIVER_PATH=${EPICS_DRIVER_PATH%:}:${EPICS_MODULES:=/ioc/modules}:${INSTBASE:=/work}/iocBoot/R$BASE/$EPICS_HOST_ARCH
export INSTBASE

# convert for win32-x86 arch
if [ ${EPICS_HOST_ARCH#win32-} != $EPICS_HOST_ARCH ]
then 
    EPICS_DRIVER_PATH=$(cygpath -wp $EPICS_DRIVER_PATH)
    DBD=$(cygpath -wp $DBD)
fi
if [ ${EPICS_HOST_ARCH#cygwin-} != $EPICS_HOST_ARCH ]
then 
    DBD=$(cygpath -wp $DBD)
fi
export EPICS_DRIVER_PATH

loadFiles () {
while [ "$#" -gt 0 ]
do
  file=$1
  case $file in
    ( -32 )
        echo "-32 option must be set earlier" >&2
        exit 1
        ;;
    ( -[1-9]* )
        echo "EPICS version $file option must be set earlier" >&2
        exit 1
        ;;
    ( -h | "-?" | -help | --help )
        help
        ;;
    ( -v | -ver | --ver | -version | --version )
        version
        ;;
    ( @* )              
        loadFiles $(cat ${file#@})
        ;;
    ( -d | -dg | --debug )
        LOADER="gdb --eval-command run --args $LOADER"
        ;;
    ( -dv )
        LOADER="valgrind --leak-check=full $LOADER"
        ;;
    ( -dp )
        LOADER="perf record $LOADER"
        ;;
    ( -c )               
        shift
        case $1 in
        ( seq* )
            if [ "$init" != NO ]
            then
                echo "iocInit"
                init=NO
            fi
            ;;
        ( iocInit )
            init=NO
            ;;
        esac
        echo $1
        ;;
    ( -s )
        shift
        if [ "$init" != NO ]
        then
            echo "iocInit"
            init=NO
        fi
        echo "seq $1"
        ;;
    ( -r )               
        shift
        echo "require $1"
        ;;
    ( -n )
        shift
        IOC="$1"
        ;;
    ( -* )
        echo "Unknown option $1" >&2
        echo "Try: $(basename $0) --help" >&2
        exit 1
       ;;
    ( *.so )
        if [ "$BASECODE" -ge 31412 ]
        then
            echo "dlload \"$file\""
        else
            echo "ld \"$file\""
        fi
        ;;
    ( *=* )
        echo -n $file | awk -F '=' '{printf "epicsEnvSet %s '\''%s'\''\n", $1, $2}'
        ;;
    ( * )
        subst=""
        while [ "$#" -gt 1 ]
        do
            case $2 in
                ( *=* )
                    subst="$subst,$2"; shift
                    ;;
                ( * )
                    break
                    ;;
            esac
        done
        subst=${subst#,}
        case $file in
            ( *.db | *.template)
                echo "dbLoadRecords '$file','$subst'"
                ;;
            ( *.subs | *.subst )
                echo "dbLoadTemplate '$file','$subst'"
                ;;
            ( *.dbd )
                # some dbd files must be loaded before main to take effect
                echo "dbLoadDatabase '$file','$DBD','$subst'"
                ;;
            ( * )
                if [ "$BASECODE" -ge 31500 ]
                then
                    echo "iocshLoad '$file','$subst'"
                else
                    echo -n $subst | awk -F '=' -v 'RS=,' '{printf "epicsEnvSet %s '\''%s'\''\n", $1, $2}'
                    echo "< '$file'"
                fi
                if grep -q iocInit $file; then init=NO; fi
                ;;
        esac
        ;;
  esac
  shift
done
}

startup=/tmp/iocsh.startup.$$

# clean up and kill the softIoc when killed by any signal
trap "kill -s SIGTERM 0; (stty sane && echo) 2>/dev/null; rm -f $startup; " EXIT

{
echo "# date=\"$(date)\""
echo "# user=\"${USER:-$(whoami)}\""
for var in IOC PWD BASE EPICS_HOST_ARCH SHELLBOX EPICS_CA_ADDR_LIST EPICS_DRIVER_PATH
do
    echo "# $var=\"${!var}\""
done

LIBPREFIX=lib
LIBPOSTFIX=.so

if [ -d $EPICS_MODULES/${REQUIRE:=require} ]
then # new module pool model
    REQUIRE_LIB=$(ls -1rv $EPICS_MODULES/$REQUIRE/${REQUIRE_VERSION:-*.*.*}/R$BASE/lib/$EPICS_HOST_ARCH/$LIBPREFIX$REQUIRE$LIBPOSTFIX | head -n 1)
    REQUIRE_DBD=${REQUIRE_LIB%/lib/*}/dbd/$REQUIRE.dbd
else # old driver pool model
    REQUIRE=misc${REQUIRE_VERSION:+-}$REQUIRE_VERSION
    REQUIRE_LIB=$INSTBASE/iocBoot/R$BASE/$EPICS_HOST_ARCH/$LIBPREFIX$REQUIRE$LIBPOSTFIX
    REQUIRE_DBD=$INSTBASE/iocBoot/R$BASE/dbd/$REQUIRE.dbd
fi

if [ "$BASECODE" -ge 31412 ]
then
    EXE=$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc
    ARGS="-D $EPICS_BASE/dbd/softIoc.dbd"
    LDCMD="dlload"
else
    # get rid of the compiled-in rpath because at PSI that is a link pointing to current EPICS version.
    LOADER="$LOADER /lib/ld-linux.so.2"
    LOADERARGS="--library-path $EPICS_BASE/lib/$EPICS_HOST_ARCH --inhibit-rpath ''"
    APP=ioc
    EXE=$EPICS_EXTENSIONS/bin/$EPICS_HOST_ARCH/$APP
    DBD=$EPICS_EXTENSIONS/dbd
    LDCMD="ld"
    echo "dbLoadDatabase \"$APP.dbd\",\"$DBD\""
    echo "${APP}_registerRecordDeviceDriver(pdbbase)"
fi

if [ ! -x $EXE ]
then
    echo "$EXE not found or not executable." >&2
    exit 1
fi

if [ ! -f "$REQUIRE_LIB" ]
then
    echo "Library $REQUIRE_LIB not found." >&2
    echo "Command 'require' is not available." >&2
else
    echo "$LDCMD $REQUIRE_LIB"
    echo "dbLoadDatabase $REQUIRE_DBD"
    echo "${REQUIRE%-*}_registerRecordDeviceDriver"
    echo "require misc $MISC_VERSION"
fi

loadFiles "$@"
if [ "$init" != NO ]
then
    echo "iocInit"
fi
if [ "$SHELLBOX" ]
then
  PATH=$PATH:/home/ioc/bin
  export BCAST_ADDR=$(/sbin/ifconfig | awk -F '[ :]+' '/Bcast/ {print $6; exit}')
  echo "! rm -f /tmp/${IOC}.dbl"
  echo 'dbl "","RTYP DESC" > /tmp/${IOC}.dbl'
  echo "! dbl2odb.sh ${IOC} \"$BCAST_ADDR\" \"$EPICS_CA_SERVER_PORT\""
  echo "! rm -f /tmp/${IOC}.libs"
  echo "libversionShow > /tmp/${IOC}.libs"
  echo "! upload_libinfo.py -i ${IOC} -l /tmp/${IOC}.libs"
fi
echo 'epicsEnvSet IOCSH_PS1,"${IOC}> "'
} > $startup

# convert startup script file name for win32-x86
if [ ${EPICS_HOST_ARCH#win32-} != $EPICS_HOST_ARCH ]
then
    startup=`cygpath -w $startup`
fi

if [ ${EPICS_HOST_ARCH#win32-} != $EPICS_HOST_ARCH -o ${EPICS_HOST_ARCH#cygwin-} != $EPICS_HOST_ARCH ]
then 
    PATH=$INSTBASE/iocBoot/R$BASE/$EPICS_HOST_ARCH:$EPIC_BASE/bin/$EPICS_HOST_ARCH:$EPICS_BASE/../seq/bin/$EPICS_HOST_ARCH:$PATH
fi

echo $EXE $ARGS $startup
#enable core dumps
ulimit -c unlimited
eval "$LOADER $LOADERARGS $EXE" $ARGS "$startup" 2>&1
