#!/bin/bash
################################################################################
# usage: linuxcnc [options] [<ini-file>]
#
# options:
#     -v = verbose - prints info as it works
#     -d = echos script commands to screen for debugging
#
# this version calls pickconfig.tcl to pick an ini file if one
# is not specified on the command line
#
################################################################################
# Author:
# License: GPL Version 2
# System: Linux
#    
# Copyright (c) 2004-2009 All rights reserved.
################################################################################

################################################################################
# 0. Values that come from configure
################################################################################
prefix=/usr
exec_prefix=${prefix}

LSMOD=@LSMOD@
PIDOF="/bin/pidof -x"
PS=/bin/ps
AWK=/usr/bin/awk
IPCS=/usr/bin/ipcs
KILL=/bin/kill

LINUXCNC_HOME=/usr; export LINUXCNC_HOME

LINUXCNC_BIN_DIR=/usr/bin
LINUXCNC_TCL_DIR=/usr/lib/tcltk/linuxcnc
LINUXCNC_HELP_DIR=/usr/share/doc/linuxcnc
LINUXCNC_RTLIB_DIR=/usr/lib/linuxcnc
LINUXCNC_CONFIG_PATH="~/machinekit/configs:/etc/linuxcnc/configs:/usr/share/linuxcnc/examples/sample-configs"
LINUXCNC_NCFILES_DIR=/usr/share/linuxcnc/ncfiles
LINUXCNC_LANG_DIR=/usr/share/linuxcnc/tcl/msgs
REALTIME=${exec_prefix}/bin/realtime
LINUXCNC_IMAGEDIR=/usr/share/linuxcnc
LINUXCNC_TCL_LIB_DIR=/usr/lib/tcltk/linuxcnc

# if not overridden by user, point MACHINEKIT_INI to installed default:
if [ -z "$MACHINEKIT_INI" ]; then
    MACHINEKIT_INI=/etc/linuxcnc/machinekit.ini
fi
export MACHINEKIT_INI

if [ ! -d "$HOME/machinekit" ]; then
    ## if already linuxcnc dir link to it
    if [ -d "$HOME/linuxcnc" ]; then
	ln -s $HOME/linuxcnc $HOME/machinekit
	echo "Creating machinekit directory link"
    else
        mkdir -p $HOME/machinekit
	echo "Creating machinekit directory"
        chown -R $USER:$USER $HOME/machinekit
    fi
fi

if [ ! -f "$HOME/.machinekitrc" ]; then
    ## if already .linuxcncrc file link to it
    if [ -f "$HOME/.linuxcncrc" ]; then
	ln -s $HOME/.linuxcncrc $HOME/.machinekitrc
	echo "Creating .machinekitrc link"
    # else will be created by pickconfig when used
    fi
fi


#put the LINUXCNC_BIN_DIR in PATH
PATH=$LINUXCNC_BIN_DIR:$PATH
#ditto scripts if not RIP
[ -d $LINUXCNC_HOME/scripts ] && PATH=$LINUXCNC_HOME/scripts:$PATH

if test "xyes" = "xno"; then
	if [ -z "$LD_LIBRARY_PATH" ]; then
	    LD_LIBRARY_PATH=$LINUXCNC_HOME/lib
	else
	    LD_LIBRARY_PATH=$LINUXCNC_HOME/lib:"$LD_LIBRARY_PATH"
	fi
	export LD_LIBRARY_PATH

	if [ -z "$PYTHONPATH" ]; then
	    PYTHONPATH=$LINUXCNC_HOME/lib/python
	else
	    PYTHONPATH=$LINUXCNC_HOME/lib/python:"$PYTHONPATH"
	fi
	export PYTHONPATH
fi

#PRELOAD_WORKAROUND=@PRELOAD_WORKAROUND@


XFILESEARCHPATH=%D:/usr/%T/%N%C:/usr/%T/%N
export XFILESEARCHPATH

MODULE_EXT=@MODEXT@ # module extension, used when insmod'ing

DEBUG_FILE=$(mktemp /tmp/linuxcnc.debug.XXXXXX)
PRINT_FILE=$(mktemp /tmp/linuxcnc.print.XXXXXX)
ln -sf $PRINT_FILE /tmp/linuxcnc.print
ln -sf $DEBUG_FILE /tmp/linuxcnc.debug

program_available () {
    type -path "$1" > /dev/null 2>&1
}

usage () {
	P=${0##*/}
	cat <<EOF
$P: Run MACHINEKIT

Usage:
	$P [-d] [-v] [-V]
		Choose the configuration file graphically

	$P [-k] [-d] [-v] [-V] path/to/your.ini
		Name the configuration file using its path

	$P [-k] [-d] [-v] [-V] -l
		Use the previous configuration file

	-d: Turn on "debug" mode
	-v: Turn on "verbose" mode
	-V: Turn on "print lots of junk" mode in halcmd
        -k: Continue in the presence of errors in .hal files
EOF

}



################################################################################
# 1.1. strip and process command line options
################################################################################
while getopts "dvVlhkr" opt
do
	case "$opt" in
	d)
		# enable echoing of script and command output
		if tty -s; then
		    DEBUG_FILE=/dev/fd/2
		    echo "Debug mode on" >$DEBUG_FILE
		fi
		set -x;;
	v)
		# enable printing of verbose messages
		if tty -s; then
		    PRINT_FILE=/dev/fd/1
		    echo "Verbose mode on" >$PRINT_FILE
		fi;;

	V)
		# enable printing of 'lots of junk' from halcmd
		if tty -s; then
		    echo "halcmd 'print lots of junk' mode on" >$PRINT_FILE
		fi
		DASHV=-V;;

        r)
                RUNTESTS=yes
                ;;

	l)
		USE_LAST_INIFILE=1;;
        k)      DASHK=-k;;
	h)
		usage
		exit 0;;
	*)
		usage
		exit 1
	esac
done
shift $(($OPTIND-1))


if ! tty -s; then
    exec 2>> $DEBUG_FILE
    exec >> $PRINT_FILE
fi

function ErrorCheck () {
    result=$?
    if [ ! -z "$DISPLAY" ]; then
        echo "catch {send -async popimage destroy .}; destroy ." | /usr/bin/wish8.6
    fi

    if [ $result -ne 0 ]; then
        if tty -s || [ -z "$DISPLAY" ] ; then
            if [ -f $DEBUG_FILE ]; then
                cp $DEBUG_FILE $HOME/linuxcnc_debug.txt
            else
                echo "(debug information was sent to stderr)" \
                    > $HOME/linuxcnc_debug.txt
            fi

            if [ -f $PRINT_FILE ]; then
                cp $PRINT_FILE $HOME/linuxcnc_print.txt
            else
                echo "(print information was sent to stdout)" \
                    > $HOME/linuxcnc_print.txt
            fi

            echo "\
Machinekit terminated with an error.  For simple cases more information
can be found in the following files:
    $HOME/linuxcnc_debug.txt
    $HOME/linuxcnc_print.txt

For other cases get more meaningfull information by restarting after
    export DEBUG=5

and look at the output of:
    /var/log/linuxcnc.log
    dmesg

When looking for errors, specifically look for libraries that fail to load
by looking for lines with 'insmod failed' as per example below.

insmod failed, returned -1:
do_load_cmd: dlopen: nonexistant-component.so: cannot open shared object file:
No such file or directory

For getting help, please have a look here: www.machinekit.io/docs/getting-help/"
        else
            /usr/bin/wish8.6 $LINUXCNC_TCL_DIR/show_errors.tcl $DEBUG_FILE $PRINT_FILE
        fi
    fi
    rm -f $DEBUG_FILE $PRINT_FILE 2>/dev/null
    exit $result
}

trap ErrorCheck EXIT

################################################################################
# 1.3. INIFILE                           find inifile to use                   #
################################################################################

if [ ! -z "$1" ]; then
    case "$1" in
      -)  USE_LAST_INIFILE=1;;
      /*) INIFILE="$1" ;;
      *)  INIFILE="`pwd`/$1";;
    esac
    shift
fi
EXTRA_ARGS="$@"

# 1.3.1. Determine if we have run-in place or installed system
RUN_IN_PLACE=no
echo RUN_IN_PLACE=$RUN_IN_PLACE >>$PRINT_FILE

LINUXCNCVERSION="0.1"; export LINUXCNCVERSION

# common from here..
INIVAR=${exec_prefix}/libexec/linuxcnc/inivar
HALCMD="halcmd $DASHK $DASHV"
PICKCONFIG="/usr/bin/wish8.6 $LINUXCNC_TCL_DIR/bin/pickconfig.tcl"
LINUXCNC_EMCSH=/usr/bin/wish8.6

echo LINUXCNC_DIR=$LINUXCNC_DIR >>$PRINT_FILE
echo LINUXCNC_BIN_DIR=$LINUXCNC_BIN_DIR >>$PRINT_FILE
echo LINUXCNC_TCL_DIR=$LINUXCNC_TCL_DIR >>$PRINT_FILE
echo LINUXCNC_SCRIPT_DIR=$LINUXCNC_SCRIPT_DIR >>$PRINT_FILE
echo LINUXCNC_RTLIB_DIR=$LINUXCNC_RTLIB_DIR >>$PRINT_FILE
echo LINUXCNC_CONFIG_DIR=$LINUXCNC_CONFIG_DIR >>$PRINT_FILE
echo LINUXCNC_LANG_DIR=$LINUXCNC_LANG_DIR >>$PRINT_FILE
echo INIVAR=$INIVAR >>$PRINT_FILE
echo HALCMD=$HALCMD >>$PRINT_FILE
echo LINUXCNC_EMCSH=$LINUXCNC_EMCSH >>$PRINT_FILE

#export some common directories, used by some of the GUI's
export LINUXCNC_TCL_DIR
export LINUXCNC_EMCSH
export LINUXCNC_HELP_DIR
export LINUXCNC_LANG_DIR
export REALTIME
export HALCMD

[ -z $RUNTESTS ] && echo "MACHINEKIT - $LINUXCNCVERSION" 

# was an inifile specified on the command line?
if [ ! -z "$USE_LAST_INIFILE" ]; then
    INIFILE=$($INIVAR -ini ~/.machinekitrc -var LAST_CONFIG -sec PICKCONFIG 2>>$DEBUG_FILE)
    echo "Using previous inifile: $INIFILE" >> $PRINT_FILE
fi

if [ ! -n "$INIFILE" ] ; then
    # nothing specified, get from the user
    # it returns either a path, or nothing at all
    INIFILE=$($PICKCONFIG)
    # if name is xxxx.demo, then:
    #    execute xxxx.demo in background and exit
    if [ "${INIFILE%%.demo}".demo = "${INIFILE}" ] ; then
      "${INIFILE}" &
      exit 0
    fi
fi

if [ ! -n "$INIFILE" ] ; then
    # still nothing specified, exit
    exit 0
else
    # make it easier for gremlin_view to get the active ini file
    export INIFILE
fi

# delete directories from path, save name only
INI_NAME="${INIFILE##*/}"
INI_DIR="${INIFILE%/*}"
CONFIG_DIR="${INIFILE%/*}"
export CONFIG_DIR

function handle_includes () {
  hdr="# handle_includes():"
  inifile=$1 
  cd $(dirname $inifile) ;# for the function() subprocess only
  grep "^#INCLUDE" $inifile >/dev/null
  status=$?
  if [ $status -ne 0 ] ; then
    echo "$inifile" ;# just use the input
    return 0 ;# ok
  fi
  outfile=`mktemp`; # Let the shell create a temporary file
  >|$outfile
  echo "#*** $outfile" >>$outfile
  echo "#*** Created: $(date)" >>$outfile
  echo "#*** Autogenerated file with expanded #INCLUDEs" >>$outfile
  echo "" >>$outfile
  line=0
  while read a b ; do
    line=$((line + 1))
    if [ "$a" = "#INCLUDE" ] ; then
       if [ "X$b" = "X" ] ; then
          msg="$hdr <$line> found #INCLUDE with no filename" >>$outfile
          echo "$msg" >&2
          rm -f $outfile
          return 1 ;# error
       else
          # expand file name
          breal=$(eval echo "$b")
          # -r: readable
          if  [ -r "$breal" ] ; then
            echo "" >>$outfile
            echo "#*** Begin #INCLUDE file: $breal" >>$outfile
            cat "$breal" >>$outfile
            echo "#*** End   #INCLUDE file: $breal" >>$outfile
          else
            msg="$hdr <$line> CANNOT READ $breal"
            echo "$msg" >&2
            rm -f $outfile
            return 1 ;# error
          fi
       fi
    else
       echo "$a $b" >> $outfile
    fi
  done <$inifile
  echo $outfile ;# use the expanded file
  return 0 ;# ok
}

PREVINIFILE=$INIFILE
INIFILE=$(handle_includes $INIFILE)
if [ $? -ne 0 ] ; then
    exit 1 ; # error
fi
if [ "$PREVINIFILE" != "$INIFILE" ]; then
    trap "rm -f $INIFILE" 0 1 2 3 15; # Clean up the temporary file
fi

export PATH=$CONFIG_DIR/bin:$PATH

[ -z $RUNTESTS ] && echo "Machine configuration directory is '$INI_DIR'"
echo "Machine configuration file is '$INI_NAME'"

# make sure ini file exists (the tcl script just did this, so we could 
# eliminate this test, but it does no harm)

if [ ! -f "$INIFILE" ] ; then
    echo "Could not find ini file '$INIFILE'"
    trap '' EXIT
    exit -1
fi
echo INIFILE=$INIFILE >>$PRINT_FILE

################################################################################
# 2.  extract info from the ini file that we will need later
################################################################################
retval=

# 2.1. define helper function
function GetFromIniQuiet {
    #$1 var name   $2 - section name
    name=$1
    retval=`$INIVAR -ini "$INIFILE" -var $1 -sec $2 2> /dev/null`
    if [ ! -n "$1" ] ; then
	exit -1
    fi
    echo "$name=$retval" >>$PRINT_FILE
}

function GetFromIni {
    #$1 var name   $2 - section name
    name=$1
    retval=`$INIVAR -ini "$INIFILE" -var $1 -sec $2 2>>$DEBUG_FILE`
    if [ ! -n "$1" ] ; then
	echo "Can't find variable $1 in section [$2] of file $INIFILE."
	exit -1
    fi
    echo "$name=$retval" >>$PRINT_FILE
}

# Usage:
#  GetFromIniEx VAR1 SEC1 [VAR2 SEC2...VARn SECn] [default]
function GetFromIniEx {
    original_var="[$2]$1"
    while [ $# -ge 2 ]; do
	if retval=`$INIVAR -ini "$INIFILE" -var "$1" -sec "$2" 2>/dev/null`; then return; fi
	shift 2
    done
    if [ $# -eq 0 ]; then
	echo "Can't find $original_var in $INIFILE."
	exit -1
    fi
    retval="$1"
}

# 2.2. get param file
GetFromIni PARAMETER_FILE RS274NGC 
RS274NGC_PARAMFILE=$retval

# 2.3. get mot information
GetFromIniEx MOT MOT EMCMOT EMCMOT motmod
EMCMOT=$retval$MODULE_EXT # add module extension

# 2.4. get io information
GetFromIniEx IO IO EMCIO EMCIO io
EMCIO=$retval

# 2.5. get task information
GetFromIni TASK TASK
EMCTASK=$retval

if [ "$EMCTASK" = emctask ]; then EMCTASK=linuxcnctask; fi



# 2.6. we hardcode the server name, change if needed
# linuxcncsvr now holds/creates all the NML channels,
# so it needs to start by default, as the first process
EMCSERVER=linuxcncsvr

# 2.7. get halui information
GetFromIniQuiet HALUI HAL
HALUI=$retval

# 2.9. get display information
GetFromIni DISPLAY DISPLAY
EMCDISPLAY=`(set -- $retval ; echo $1 )`
EMCDISPLAYARGS=`(set -- $retval ; shift ; echo $* )`

case $EMCDISPLAY in
    tkemc) EMCDISPLAY=tklinuxcnc ;;
    xemc) EMCDISPLAY=xlinuxcnc ;;
esac

# 2.10. get NML config information
GetFromIniEx NML_FILE LINUXCNC NML_FILE EMC /usr/share/linuxcnc/linuxcnc.nml
NMLFILE=$retval
export NMLFILE

################################################################################
# 3. Done gathering information, define a few functions
# Execution resumes after function definitions...
################################################################################

KILL_TASK=
KILL_TIMEOUT=20

################################################################################
# 3.1. Kills a list of tasks with timeout
# if it doesn't work, kill -9 is used
################################################################################
function KillTaskWithTimeout() {
    if [ ! -n "$KILL_PIDS" ] ; then
	KILL_PIDS=`$PIDOF $KILL_TASK`
    fi
    if [ ! -n "$KILL_PIDS" ] ; then
	echo "Could not find pid(s) for task $KILL_TASK"
	return -1
    fi
    for KILL_PID in $KILL_PIDS ; do
	echo "Killing task $KILL_TASK, PID=$KILL_PID" >>$PRINT_FILE
	# first a "gentle" kill with signal TERM
	$KILL $KILL_PID
	WAIT=$KILL_TIMEOUT
	# wait and see if it dissappears
	while [ $WAIT -gt 1 ] ; do
	    # see if it's still alive
	    if $PS $KILL_PID >>$DEBUG_FILE ; then
		WAIT=$(($WAIT-1))
		sleep .1
	    else
		WAIT=0
	    fi
	done
	if [ $WAIT -gt 0 ] ; then
	    # gentle didn't work, get serious
	    echo "Timeout, trying kill -9" >>$PRINT_FILE
	    $KILL -9 $KILL_PID
	    WAIT=$KILL_TIMEOUT
	    # wait and see if it dissappears
	    while [ $WAIT -gt 1 ] ; do
		# see if it's still alive
		if $PS $KILL_PID >>$DEBUG_FILE ; then
		    WAIT=$(($WAIT-1))
		    sleep .1
		else
		    WAIT=0
		fi
	    done
	fi
	if [ $WAIT -gt 0 ] ; then
	    echo "Could not kill task $KILL_TASK, PID=$KILL_PID"
	fi
	KILL_PIDS=
	KILL_TASK=
    done
}


################################################################################
# 3.2. define the cleanup function
#
# this cleanup function doesn't know or care what was actually
# loaded - it simply kills _any_ processes in its list of
# components
################################################################################
function Cleanup() {

    echo "Shutting down and cleaning up Machinekit..."
    # Kill displays first - that should cause an orderly
    #   shutdown of the rest of linuxcnc
    for KILL_TASK in xlinuxcnc ylinuxcnc linuxcncpanel keystick iosh linuxcncsh linuxcncrsh linuxcnctop mdi debuglevel; do
	if $PIDOF $KILL_TASK >>$DEBUG_FILE ; then
	    KillTaskWithTimeout
	fi
    done

    if program_available axis-remote ; then
	if [ ! -z "$DISPLAY" ]; then
	    axis-remote --ping && axis-remote --quit
	fi
    fi

    if [ "$1" = "other" ]; then
        echo -n "Waiting for other session to finish exiting..."
	WAIT=$KILL_TIMEOUT
	while [ $WAIT -gt 1 ]; do
	    if ! LinuxcncRunning ; then
#            if ! [ -f $LOCKFILE ]; then
                echo " Ok"
                return 0
            fi
            WAIT=$(($WAIT-1))
            sleep .1
        done
        echo "lockfile still not removed"
    fi

    SHUTDOWN=`$INIVAR -ini "$INIFILE" -var SHUTDOWN -sec HAL 2> /dev/null`
    if [ -n "$SHUTDOWN" ]; then
	echo "Running HAL shutdown script"
	$HALCMD -f $SHUTDOWN
    fi

    # now kill all the other user space components
    for KILL_TASK in linuxcncsvr milltask; do
	if $PIDOF $KILL_TASK >>$DEBUG_FILE ; then
	    KillTaskWithTimeout
	fi
    done

    echo "Stopping realtime threads" >> $DEBUG_FILE
    $HALCMD stop
    echo "Unloading hal components" >> $DEBUG_FILE
    $HALCMD unload all

    for i in `seq 10`; do
        # (the one component is the halcmd itself)
        if [ "`$HALCMD list comp | wc -w`" = 1 ]; then break; fi
        sleep .2
    done

    echo "Removing HAL_LIB, RTAPI, and Real Time OS modules" >>$PRINT_FILE
    $REALTIME stop

    echo "Removing NML shared memory segments" >> $PRINT_FILE
    while read b x t x x x x x x m x; do
        case $b$t in
            BSHMEM) ipcrm -M $m 2>/dev/null;;
        esac
    done < $NMLFILE


    # # remove lock file
    # if [ -f $LOCKFILE ] ; then
    # 	rm $LOCKFILE
    # fi

    echo "Cleanup done"
}

# this needs urgent repair 
# no more sysvipc shm, and shmdrv might be present
# see realtime.in -mah
function LinuxcncRunning() {
    # 135612177 is the integer ID of the GLOBAL data segment:
    # (GLOBAL_KEY 0x08154711). it is universally present for 
    # all thread styles build options, and removed by 
    # RTAPI rtapi_app_exit(). Hence it is a more reliable
    # indication for 'RTAPI running' than a lock file.
    grep 135612177  /proc/sysvipc/shm >/dev/null 2>&1
}

################################################################################
# 4. done with function definitions, execution resumes here
################################################################################

# Name of lock file to check for that signifies that Machinekit is up,
# to prevent multiple copies of controller
LOCKILE=/tmp/linuxcnc.lock

# Check for lock file
if LinuxcncRunning ; then
  if tty -s; then
    echo -n "Machinekit is still running.  Restart it? [Y/n] "
    read input; [ -z $input ] && input=y
  else
    input=$(/usr/bin/wish8.6 <<EOF
wm wi .
puts [tk_messageBox -title Machinekit -message "Machinekit is still running.  Restart it?" -type yesno]
exit
EOF
)
  fi
  case $input in
    y|Y|yes)
      echo Cleaning up old Machinekit...
      Cleanup other
    ;;
    *)
      echo Not starting new Machinekit
      exit 0
    ;;
  esac
fi
echo Starting Machinekit...

# trap ^C so that it's called if user interrupts script
trap 'Cleanup ; exit 0' SIGINT SIGTERM

# go to the dir where the ini file is
# either configs/<specific-config> when run-in-place, or
# /usr/local/share/linuxcnc/configs/<specific-config> (wherever it was installed)
cd "$INI_DIR"

# Create the lock file
#touch $LOCKFILE

################################################################################
# 4.1. pop up intro graphic
################################################################################
img=`$INIVAR -ini "$INIFILE" -var INTRO_GRAPHIC -sec DISPLAY 2>>$DEBUG_FILE`
imgtime=`$INIVAR -ini "$INIFILE" -var INTRO_TIME -sec DISPLAY 2>>$DEBUG_FILE`
if [ "$imgtime" = "" ] ; then
  imgtime=5
fi
if [ "$img" != "" ] ; then
  if [ -e "$img" ]; then
    true
  elif [ -e "$INI_DIR/$img" ]; then
    img="$INI_DIR/$img"
  elif [ -e "$LINUXCNC_IMAGEDIR/$img" ]; then
    img="$LINUXCNC_IMAGEDIR/$img"
  else
    img=
  fi
fi
if [ "$img" != "" ] ; then
    if [ -x $LINUXCNC_TCL_DIR/bin/popimage ] ; then
        $LINUXCNC_TCL_DIR/bin/popimage $img $imgtime &
    fi
fi
 
################################################################################
# 4.2. Now we can finally start loading Machinekit
################################################################################

# 4.3.1. Run linuxcncserver in background, always (it owns/creates the NML buffers)
echo "Starting Machinekit server program: $EMCSERVER" >>$PRINT_FILE
if ! program_available $EMCSERVER; then
    echo "Can't execute server program $EMCSERVER"
    Cleanup
    exit 1
fi
export INI_FILE_NAME="$INIFILE"
$EMCSERVER -ini "$INIFILE" &

# 4.3.2. Start REALTIME
echo "Loading Real Time OS, RTAPI, and HAL_LIB modules" >>$PRINT_FILE
if ! $REALTIME start ; then
    echo "Realtime system did not load"
    Cleanup
    exit -1
fi

# 4.3.3. export the location of the HAL realtime modules so that
# "halcmd loadrt" can find them
export HAL_RTMOD_DIR=$LINUXCNC_RTLIB_DIR

# 4.3.4. Run io in background if so defined in INI
if [ "$EMCIO" != "" ] ; then
        echo "Starting Machinekit IO program: $EMCIO" >>$PRINT_FILE
        if ! program_available $EMCIO ; then
                echo "Can't execute IO program $EMCIO"
                Cleanup
        exit 1
        fi
        echo $EMCIO started
        $HALCMD loadusr -Wn iocontrol $EMCIO -ini "$INIFILE"
        echo halcmd loadusr $EMCIO started
else
        echo "Skipping Machinekit IO program >>$PRINT_FILE"
fi

# 4.3.5. Run halui in background, if necessary
if [ -n "$HALUI" ] ; then
    echo "Starting HAL User Interface program: $HALUI" >>$PRINT_FILE
    if ! program_available $HALUI ; then
	echo "Can't execute halui program $HALUI"
	Cleanup
	exit 1
    fi
    $HALCMD loadusr -Wn halui $HALUI -ini "$INIFILE"
fi

TWOPASS=`$INIVAR -ini "$INIFILE" -var TWOPASS -sec HAL -num 1 2> /dev/null`
if [ -n "$TWOPASS" ] ; then
  # if [HAL]TWOPASS is defined, handle all [HAL]HALFILE entries here:
  CFGFILE=/usr/lib/tcltk/linuxcnc/twopass.tcl
  if ! haltcl -i "$INIFILE" $CFGFILE && [ "$DASHK" = "" ]; then
      Cleanup
      exit -1
  fi
else
    # 4.3.6. execute HALCMD config files (if any)
    # get first config file name from ini file
    NUM=1
    CFGFILE=`$INIVAR -tildeexpand -ini "$INIFILE" -var HALFILE -sec HAL -num $NUM 2> /dev/null`
    while [ -n "$CFGFILE" ] ; do
        case $CFGFILE in
        *.tcl)
            if ! haltcl -i "$INIFILE" $CFGFILE && [ "$DASHK" = "" ]; then
                Cleanup
                exit -1
            fi
        ;;
        *.py)
            if ! python2 $CFGFILE ; then
                Cleanup
                exit -1
            fi
        ;;
        *)
            if ! $HALCMD -i "$INIFILE" -f $CFGFILE && [ "$DASHK" = "" ]; then
                Cleanup
                exit -1
            fi
        esac
        # get next config file name from ini file
        NUM=$(($NUM+1))
        CFGFILE=`$INIVAR -tildeexpand -ini "$INIFILE" -var HALFILE -sec HAL -num $NUM 2> /dev/null`
    done
fi

# 4.3.7. execute discrete HAL commands from ini file (if any)
# get first command from ini file
NUM=1
HALCOMMAND=`$INIVAR -ini "$INIFILE" -var HALCMD -sec HAL -num $NUM 2> /dev/null`
while [ -n "$HALCOMMAND" ] ; do
    if [ -n "$HALCOMMAND" ] ; then
	echo "Running HAL command: $HALCOMMAND" >>$PRINT_FILE
	if ! $HALCMD $HALCOMMAND && [ "$DASHK" = "" ]; then
	    echo "ini file HAL command $HALCOMMAND failed."
	    Cleanup
	    exit -1
	fi
    fi
    # get next command from ini file
    NUM=$(($NUM+1))
    HALCOMMAND=`$INIVAR -ini "$INIFILE" -var HALCMD -sec HAL -num $NUM 2> /dev/null`
done

# 4.3.8. start the realtime stuff ticking
$HALCMD start

# 4.3.9. Run task in background
echo "Starting TASK program: $EMCTASK" >>$PRINT_FILE
if ! program_available $EMCTASK ; then
    echo "Can't execute TASK program $EMCTASK"
    Cleanup
    exit 1
fi

# setup LD_PRELOAD  if deemed necessary by configure
unset LD_PRELOAD
if [ -n "$PRELOAD_WORKAROUND" ] ; then
	export LD_PRELOAD=$PRELOAD_WORKAROUND
fi

$EMCTASK -ini "$INIFILE" &
echo task pid=$!

# 4.3.10. Run display in foreground
echo "Starting DISPLAY program: $EMCDISPLAY" >>$PRINT_FILE
result=0
case $EMCDISPLAY in
  tklinuxcnc|mini)
    # tklinuxcnc and mini are in the tcl directory, not the bin directory
    if [ ! -x $LINUXCNC_TCL_DIR/$EMCDISPLAY.tcl ] ; then
	echo "Can't execute DISPLAY program $LINUXCNC_TCL_DIR/$EMCDISPLAY.tcl $EMCDISPLAYARGS"
	Cleanup
	exit 1
    fi
    $LINUXCNC_TCL_DIR/$EMCDISPLAY.tcl -ini "$INIFILE" $EMCDISPLAYARGS
    result=$?
  ;;
  dummy)
    # dummy display just waits for <ENTER>
    echo "DUMMY DISPLAY MODULE, press <ENTER> to continue."
    read foo;
  ;;
  keystick)
    if ! program_available keystick ; then
	echo "Can't execute DISPLAY program $EMCDISPLAY $EMCDISPLAYARGS $EXTRA_ARGS"
	Cleanup
	exit 1
    fi
    if [ ! -z "$DISPLAY" ]; then
        xterm -xrm 'XTerm*metaSendsEscape:false' -ls -e keystick -ini "$INIFILE"
    else
        keystick -ini "$INIFILE"
    fi
    result=$?
  ;;
  *)
    # all other displays are assumed to be commands on the PATH
    if ! program_available $EMCDISPLAY; then
        echo "Can't execute DISPLAY program $EMCDISPLAY $EMCDISPLAYARGS $EXTRA_ARGS"
        Cleanup
        exit 1
    fi
    $EMCDISPLAY -ini "$INIFILE" $EMCDISPLAYARGS $EXTRA_ARGS
    result=$?
  ;;
esac

# the display won't return until you shut it down,
# so when you get here it's time to clean up
Cleanup

exit $result
