#!/usr/bin/env expect

##################################################################################
#
#  Copyright (C) 2008-2011 Craig Miller
#
#  Copyright (C) 2005-2007 Freescale Semiconductor, Inc. 
#
#  See the file "COPYING" for information on usage and redistribution
#  of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#  Distributed under BSD-Style License
#
##################################################################################
#
#
# Expect-lite 
# Script logs into Remote-host, sends command file, while validating commands
#	fails if validation fails, returns code 1 (0=passed)
#
# by Craig Miller cvmiller@gmail.com	19 Jan 2005
#
# Command Arguments:
#	<Remote-Host|IP>
#	<command_file>
#	<user_directory>
#	[additional constants, see usage]
#
#
# Requires Expect version supported 5.42.1 and later (although earlier versions may work)
#
#
#
#

##################################################################################
#	
#	The user may want to customize the following variables & procs to suite one's needs
#
#

# Encapsulate expect-lite variables in a namespace to prevent collisions with other apps
namespace eval expectlite {

	# echo version in usage
	variable version "4.1.2"

	#set starting expect timeout 
	set ::timeout 10 

	# EL_LIBRARY when set, places expect-lite in library mode,
	# used for TCL programming calling expect-lite functions directly
	# see documentation for more info
	# Normally, this would be UNSET (or commented out)
	#set EL_LIBRARY 1

	# infinite_loop protection - Max number of looping permitted
	#	Protects against infinite loops by decrementing this number
	#	when value is Zero, _el_buffer_search will return 'not found' (-1)
	# typically this should be set in the range of 5000-10000
	variable _el_infinite_loop 5000


	# hardcoded login values - only used for telnet, and ssh logins
	variable user "root"
	variable pass ""
	# User is now used by ssh_key method as well. If user is blank then the default
	#	user will be used (the user running the script). However if $user is defined
	#	then ssh_key will use the following command: ssh $user@$host
	variable user ""


	# Print Warnings to stdout (when set to 1 = TRUE, 0 = FALSE)
	variable WARN 1

	# Print Info messages to stdout (when set to 1 = TRUE, 0 = FALSE)
	variable INFO 1

	# Print Expect Info messages to stdout (when set to 1 = TRUE, 0 = FALSE)
	variable EXP_INFO 0

	# Select remote shell to spawn after logging into remote host 
	variable remote_shell "bash"
	variable remote_shell ""

	# Delay (in ms) to wait for host in Not Expect, and Dynamic Var Capture
	# 100 ms is a good value for a local LAN, 200 ms if running across high speed internet
	variable delay_wait_for_host 100

	# connects to login host, and logs in as $USER
	#
	#	Choose one login method here
	#	
	#set connect_method "telnet" 
	#set connect_method "ssh" 
	#set connect_method "ssh_key" 
	variable connect_method "none" 

	# Path to telnet (helps in cygwin environments)
	variable _el_telnet "/usr/bin/telnet"

	# set the user configurable prompt to something sane
	set ::expectlite::_el(prompt) ".*\$ $"

	# set global color for comment ; or ;; lines
	# select one of the following: none, blue, ltblue, gray, ltgray, cyan, black, pink, purple
	variable _el_comment_color ltblue

	# End of Line Sequence (eols) - typically \n or \r\n
	# some devices must have a CRLF at the end of line in order to respond
	# This is also controlled by directive *EOLS LF|CRLF
	variable _el_eols "\n"


	####################################################################################
	#	
	#	Vars & Procs below here should not require customization
	#
	#


	#
	#	Provide info for library mode
	#
	package provide expect-lite 4.0


	#
	#	Initialization of Global Variables
	#
	proc _el_init_globals { } {

		#set ::expectlite::_el_timeout to hold value of timeout (used to preserve value of timeout)
		variable _el_timeout $::timeout

		#set ::expectlite::the expect Match Buffer really large
		match_max -d 10000

		#Create Debug Log output == 1, no log ==0
		variable DEBUG_LOG 0

		# declare array user_namespace (for user vars)
		set ::expectlite::user_namespace(0) 0

		# declare array cli_namespace (for cli passed constants)
		set ::expectlite::cli_namespace(#) " "

		# declare global array _el (for internal expect-lite vars)
		set ::expectlite::_el(0) 0

		# clear NOFAIL flag 1=test should have failed
		set ::expectlite::_el(test_failed) 0 

		# clear native expect lines list
		set ::expectlite::_el(_el_lines) {}

		# debugging aid: turn on to display match string and expect buffer
		variable DEBUG 0

		# timestamp on send: turn on to display timestamp when sending > or >>
		variable TIMESTAMP 0

		# keep script running to end: turn on to never fail test
		variable NOFAIL 0

		# global constant for timeout_multiplier mode
		variable timeout_multiplier "timeout_multiplier"

		# global constant for script name aka arg0
		variable arg0 "arg0"

		# set ::expectlite::global fork count (for multiple sessions) to 0
		set ::expectlite::_el(fork_count) 0

		# set ::expectlite::global fork stack to blank
		set ::expectlite::_el_fork_stack {}

		# set ::expectlite::global fork id list to blank
		set ::expectlite::_el_fork_id_list { }

		# set ::expectlite::default FORK session
		set ::expectlite::_el(fork_current) "default"

		# set ::expectlite::current FORK session
		variable _el_current_session ""

		# set ::expectlite::default fork_context to default end-of-line-sequence (eols)
		set ::expectlite::_el_fork(default,eols) $::expectlite::_el_eols

		#
		# Scripts (aka Command files) are kept in buffers for random access, and looping
		# Buffers are global, and are referenced by a "stack" number, the stack number allows multiple buffers to exist
		#
		# stack which contains list of valid buffers
		set ::expectlite::_el_buf(stack) "0"
		# buf pointer - contains line number of buffer
		set ::expectlite::_el_buf(0,ptr) "0"

		# expect-lite key characters (first char in expect-lite script)
		variable _out ">"
		variable _out_nowait ">>"
		variable _in "<"
		variable _in_noregex "<<"
		variable _not_in "-<"
		variable _timeout "@"
		variable _exec "!"
		variable _stdout_comment ";"
		variable _stdout_comment_nobuf ";;"
		variable _include "~"
		variable _include_fail "\\\*~"
		variable _setvar "+"
		variable _var "\$"
		variable _cond_regex "\\\?"
		variable _cond "?"
		variable _cond_else "::"
		variable _label "%"
		variable _incr_var "\\\+\\\$"
		variable _decr_var "\\-\\\$"

		# cli Parameter Initialization
		variable remote_host "none"
		variable cmd_file ""
		variable user_dir ""

		# set ::expectlite::the default user configurable prompt to something sane
		set ::expectlite::_el(default_prompt) ".*\$ $"

		# set ::expectlite::ANSI colors for colored output
		set ::expectlite::el_color(red) "\033\[1;31m"
		set ::expectlite::el_color(blue) "\033\[0;34m"
		set ::expectlite::el_color(ltblue) "\033\[1;34m"
		set ::expectlite::el_color(black) "\033\[0;30m"
		set ::expectlite::el_color(cyan) "\033\[0;36m"
		set ::expectlite::el_color(gray) "\033\[1;30m"
		set ::expectlite::el_color(ltgray) "\033\[0;37m"
		set ::expectlite::el_color(green) "\033\[0;32m"
		set ::expectlite::el_color(yellow) "\033\[0;33m"
		set ::expectlite::el_color(purple) "\033\[0;35m"
		set ::expectlite::el_color(pink) "\033\[1;35m"
		set ::expectlite::el_color(nocolor) "\033\[0m"
		set ::expectlite::el_color(none) ""

		# set ::expectlite::INFO color
		variable _el_info_color green
		# set ::expectlite::WARN color
		variable _el_warn_color yellow
		# set ::expectlite::ERROR color
		variable _el_err_color red

		# el_exec vars
		variable comment_line ""
		variable timeout_line ""
		
		# 
		#	Expect Signal Handler, which checks for broken stdout pipe
		#	Default signal is SIGPIPE
		#
		#
		trap {
			catch { puts stderr "\nERROR:Broken Pipe\n" }
			exit 2
		} SIGPIPE
		# 
		#	Expect Signal Handler, which places user into instant-interactive(tm) session
		#	Default signal is SIGQUIT or ^\
		#
		#
		trap {
			expectlite::instant_interact_sig
		} SIGQUIT

	}

	#
	#	Removes colour if terminal type is not xterm, vt100, cygwin
	#	
	#
	proc check_term_type { } {
		global  env
		upvar expectlite::_el_comment_color 		_el_comment_color
		upvar expectlite::el_color 					el_color
		upvar expectlite::_el_info_color			_el_info_color
		upvar expectlite::_el_warn_color			_el_warn_color
		upvar expectlite::_el_err_color 			_el_err_color

		#disable global comment color if unknown
		if { [lsearch [array names el_color] $_el_comment_color] == -1 } {
			set _el_comment_color none
		}

		if { [catch { switch -glob -- $env(TERM) {
			"xterm"		-
			"xterm-color"		-
			"vt100"		-
			"vt220"		-
			"ansi"		-
			"cygwin"	{ 
				# keep colors as set globally
			 }
			 default {
				# set INFO color
				set _el_info_color none
				# set WARN color
				set _el_warn_color none
				# set ERROR color
				set _el_err_color none
				# set global color
				set _el_comment_color none		 	
			 }
		} } \
		error ] } {
			puts "Warning: No Terminal Type defined: $error"
			# set INFO color
			set _el_info_color none
			# set WARN color
			set _el_warn_color none
			# set ERROR color
			set _el_err_color none
			# set global color
			set _el_comment_color none		 	

		}
	}

	#
	#	Wraps puts in selected global colour 
	#	
	#
	proc cputs { str  {l_color ""} } {
		upvar expectlite::el_color 					el_color
		upvar expectlite::_el_info_color			_el_info_color
		upvar expectlite::_el_warn_color			_el_warn_color
		upvar expectlite::_el_err_color 			_el_err_color
		upvar expectlite::_el_comment_color 		_el_comment_color

		proc protect_puts { str } {
			# catch stdout errors and quit
			if { [catch { puts $str } error] } {
				puts stderr "ERROR:$error"
				exit 1
			}
		}

		if {$_el_comment_color == "none"} {
			# use no color
			protect_puts $str	
		} elseif { $l_color != "" } {	
			# use supplied color
			protect_puts "$el_color($l_color)$str$el_color(nocolor)"
		} elseif {$_el_comment_color != "none"} {
			# use global color
			protect_puts "$el_color($_el_comment_color)$str$el_color(nocolor)"
		} 

	}


	# 
	#	Interact help
	#	
	#
	#
	proc interact_help { } {
		send_user "\n"
		send_user "IDE: Help\n"
		send_user "  Key          Action\n"
		send_user "  ----        ------\n"
		send_user "  <esc>s      Step\n"
		send_user "  <esc>k      sKip next step\n"
		send_user "  <esc>c      Continue\n"
		send_user "  <esc>v      show Vars\n"
		send_user "  <esc>0to9   Show N next lines of script\n"
		send_user "  <esc>-1to-9 Show N previous lines of script\n"
		send_user "  ctrl+D      Quit & Exit expect-lite\n"
		send_user "  <esc>h      this Help\n"
		#send_user "\n"
		return
	}



	# 
	# 
	#	Actual Signal Handler, which places user into instant-interactive(tm) session
	#	Default signal is SIGQUIT or ^\
	#	This function is also called by *INTERACT, providing similar functionality
	#
	proc instant_interact_sig {} {
		global expect_out 
		upvar expectlite::remote_host		remote_host
		upvar expectlite::_el				_el
		upvar expectlite::DEBUG 			DEBUG
		upvar expectlite::NOFAIL			NOFAIL
		upvar expectlite::_el_info_color	_el_info_color
		upvar expectlite::_el_eols			_el_eols
		upvar expectlite::_el_buf			_el_buf
		upvar expectlite::_el_warn_color	_el_warn_color
		upvar expectlite::_el_err_color	_el_err_color


		# change user message depending on how the function is called
		if { $_el(line) != "*INTERACT" } {
			# called by user 
			cputs  "\n\n Press '+++' to end instant-interact & return to script \n\n" $_el_info_color
		} else {
			# called by script *INTERACT
			cputs  "\n\n Press '+++' to end interact session & return to script \n\n" $_el_info_color
		}
		# do not allow script to fail during IDE
		incr NOFAIL
		protected_send "$_el_eols"
		# preserve script fork session
		push_session_id $_el(fork_current)
		set restart_interact 1
		# restart interact if *FORK is called to set to new spawn_id
		while {$restart_interact} {
			# Interact session connects user to remote session, and also:
			#	interprets *FORK, *SHOW commands
			#	Experimental: also interprets subset of expect-lite script commands
			#		Lines starting with >,?,$,~,;,* 
			#			send, if, var assignment, include file, comment, directives respectively
			if { [catch { interact {
				-re {\+\+\+|^\x1b\x63|^`c} { #DEBUGGER '+++' or .c to exit interact session, continue script
					set restart_interact 0 
					return 
					}
				\004	{ # ^D exit interact and expect-lite
					puts "\nExiting interact & expect-lite.\n"
					exit 
					}
				-reset \005	{ # test
					puts "Current Spawn ID---->$::spawn_id|timeout:$::timeout"
					send "\n"
					return 
					}
				-re {(^\x1b|`)(s|k)}	{ #DEBUGGER .s=Next Step, k=skip step
					# determine next or skip step
					set param "s" ; # set default to step
					catch { set param "$interact_out(2,string)" } error 

					# increment buffer line pointer 
					incr _el_buf($_el_buf(stack),ptr)
					# read line from buffer
					set line  [ _el_buffer_read  $_el_buf($_el_buf(stack),ptr) $_el_buf(stack) ]
					#puts "==BUF====>$_el_buf($_el_buf(stack),ptr)||$line\n"

					switch -regexp -- $line { 
						{^\*INT}	{ 
								# skip interact lines
								cputs "$line"  $_el_info_color
								protected_send "$_el_eols"
							}
						{^\*FORK}	{ 
								# Allow interact to return, since it hangs onto old session
								if { $param == "k" } {
									cputs "Skipping:$line"  $_el_info_color
									protected_send "$_el_eols"
								} else {
									cputs "$line" $_el_info_color
									_el_script_exec "\|$line"  $_el_buf(stack)
									# allow interact to return, for new/changed session
									return
								}
							}
						^\!	{	# Send prompt after native expect lines
								if { $param == "k" } {
									cputs "Skipping:$line"  $_el_info_color
									protected_send "$_el_eols"
								} else {
									cputs "$line" $_el_info_color
									_el_script_exec "\|$line"  $_el_buf(stack)
									# kick the prompt
									protected_send "$_el_eols"

								}
							}
						^\>	{	# send line out and clump any following lines with < or +$
								# clear clump list
								set cli_list ""
								# add lines to clump
								while {$line!=-1} {
									lappend cli_list $line 
									incr _el_buf($_el_buf(stack),ptr)
									# read line from buffer
									set line  [ _el_buffer_read  $_el_buf($_el_buf(stack),ptr) $_el_buf(stack) ]
									#puts "===L==>$line"
									# is line part of clump: expect, dynvar, not-expect
									if {[regexp {^\<|^\+\$|^\-\<} $line] == 0} {
										#No more clump, roll back pointer
										incr _el_buf($_el_buf(stack),ptr) -1
										set line -1
									}
								}					
								# send clump to _el_script_exec
								#send_user "=====>$cli_list||$_el_buf(stack)"
								foreach item $cli_list {
									if { $item != "" } {
										if { $param == "k" } {
											cputs "Skipping:$item"  $_el_info_color
											protected_send "$_el_eols"
										} else {
											# execute next step
											cputs "$item"  $_el_info_color
											_el_script_exec "\|$item" $_el_buf(stack)
										}
									}
								}

								# end clump send
							}
						^\~	{	# include a file
								if { $param == "k" } {
									cputs "Skipping:$line"  $_el_info_color
									protected_send "$_el_eols"
								} else {
									cputs "$line"  $_el_info_color
									# kick the prompt
									protected_send "$_el_eols"
									_el_script_exec "\|$line"
								}
							}
						"-1" {
								cputs "End of File" $_el_warn_color
							}
						default {
								if { $param == "k" } {
									cputs "Skipping:$line"  $_el_info_color
									protected_send "$_el_eols"
								} else {
									cputs "$line" $_el_info_color
									#puts "---->$::spawn_id"
									_el_script_exec "\|$line"  $_el_buf(stack)
									
									
									if {$_el(test_failed) == 1 } {
										# step failed? kick the prompt
										protected_send "$_el_eols"
									}
								}


							}
						}
					}
				-re {^(\x1b|`)([-]?[0-9])} {	#DEBUGGER .-1to9 Show next or last 1-9 lines
						# .1to9 or .-1to9
						#send_user "=====>[scan $interact_out(0,string) %c]|$interact_out(0,string)"
						#catch { set param "$interact_out(1,string)" } error 
						catch { set param "$interact_out(2,string)" } error 

						_el_buffer_show $param $_el_buf(stack)
						protected_send "$_el_eols"
					}
				-re {^\x1bv|`v} {	#DEBUGGER .v Show Vars
						show_vars
						protected_send "$_el_eols"
					}
				-re {\x1bh|`h} {	#DEBUGGER .h Show help
						interact_help
						protected_send "$_el_eols"
					}
				-echo -re  {(^>[a-zA-Z0-9/$.]|^\?[iI][fF]|^\$[a-zA-Z0-9]|^~[a-zA-Z0-9]|^;|^\*[A-Z]|^\+\$|^=\$|%[A-Z])(.+)\r|(^@[0-9]+\r)} {
						#### Support for EL commands in Interact
						#### Permits pasting of multiple commands into Interact session
						# create list of multiple pasted commands
						set cli_list [split $interact_out(0,string) "\r"]

						# OS identification - .dll or "" = Cygwin, .so=Linux, .dylib=MacOS X
						set os_id [info sharedlibextension]
						if { $os_id == ".dll" || $os_id == "" } {
							# Special expect to pick up Cygwin Pasted lines, which come one at a time
							while 1 {
								expect_user {
									-re {(.+)\r} {
										# strip newlines and add to cli_list
										regsub {\n|\r|\r\n} $expect_out(1,string) "" cli_item
										lappend cli_list $cli_item
										} 
									-timeout 1 timeout { break }
									-re \003 {puts "\nAck!\n"; exit 1}
									}
								}
								# end while
						}
						#end if os_id
						# clear line of pasted text
						send_user "                                                                      \r"
						if {$DEBUG} {
							send_user "\nDEBUG: Evaluating:\n"
							foreach d_item $cli_list {
								send_user "\t$d_item\n"
							}
						}

						#send_user "\n=====>$cli_list\n"
						foreach item $cli_list {
							#send_user "===I==>$item\n"
							if { $item != "" } {
								# call _el_script_exe in 'one-line' mode 
								# add buf_stack context to allow jumping to labels
								#FIXME: check return code, fail if needed
								_el_script_exec "\|$item" $_el_buf(stack)
							}
						}
						protected_send "$_el_eols"
						return
					}
				} }  \
			 error] } {
				puts stderr "CONNECT ERROR: lost connection to $remote_host \n   $error"
				# Fail script?
				exit 1
			}
			protected_send "$_el_eols"
		}
		# restore script session
		session_id_manager [ pop_session_id ]
		set _el(success) 1
		protected_send "$_el_eols"
		# restore NOFAIL
		incr NOFAIL -1

	}


	#
	#	Prompts are always a problem, this should _not_ require customization with the
	#	addition of User Defined Prompts '*/some_regex/' as of version 3.1.5
	#	These prompt regexs are setup to take just about any non-color prompt: >#%$
	#
	#
	proc wait_for_prompt { } {
		global timeout expect_out
		upvar expectlite::_el_warn_color		_el_warn_color
		upvar expectlite::WARN					WARN
		upvar expectlite::DEBUG					DEBUG
		upvar expectlite::_el					_el
		
		#Accept '>' '#' '%' or '$' prompt
		if { $timeout == 0 } {
			# delay in ms
			after 50
		}
		set TIMEOUT_RETURN 0
		# User Defined prompt is $_el(prompt)
		if {[catch { expect { 
			-re "$_el(prompt)" { } 
			-re ".*> $" { } 
			-re ".*# $" { } 
			-re ".*% $" { } 
			-re {.*\$ $} { } 
			timeout { 
				if {$WARN} { cputs "Warning:Prompt Timed Out!" $_el_warn_color} 
				set TIMEOUT_RETURN 1
				# is user defined prompt being used?
				if { $_el(default_prompt) != $_el(prompt)} {
					if {$DEBUG} {
						# gobble as much return text as possible
						expect -notransfer -re ".*\n+" { }
						# print out debug info to help user 
						puts "DEBUG: User Defined Prompt:<<$_el(prompt)>>"
						puts "  Not Found in<<$expect_out(buffer)>>"
					}
				}

				} } } \
			error ] } {
				puts "\nERROR: $error: Bad User Defined Prompt: $_el(prompt)"
				# Quit expect-lite
				exit 1
		}
		if {$TIMEOUT_RETURN} { return 1 } else { return 0 }

	}




	#
	#	Print Usage
	#	
	#
	proc usage {} {
		upvar expectlite::version version
		puts "usage: expect-lite -c <command_file> \[ -r <remote_host_IP>\] \[-d <user_dir>\] \\ "
		puts "              \[const1=value1\] \[*DIRECTIVE\]"
		puts "   or: expect-lite c=<command_file> r=<remote_host_IP>  \[d=<user_dir>\]      \\ "
		puts "              \[const1=value1\] \[*DIRECTIVE\]"
		puts "   eg. ./expect-lite -c pm_alt.elt -r host8 -d /home/sw myvar=myvalue *NOFAIL"
		puts " "
		puts "   additional login options: user_name=<username>  user_password=<password>"
		puts "   eg. ./expect-lite c=pm_alternation.elt r=host-008 u=superuser p=mysecret"
		puts " "
		puts "   additional logging options: -V|-v|-vv|--verbose"
		puts "        -V        prints \'expected\' lines (i.e lines beginning with < & << )"
		puts "        -v        prints warnings, and infos (i.e. conditionals,  "
		puts "                                   dynamic var assignments)"
		puts "        -vv|--verbose   prints warnings and debug (i.e. expect match info) "
		puts "   eg. ./expect-lite -vv r=host8  c=pm_alt.elt "
		puts " "
		puts "If set, the following environment variables will be used: "
		puts "   EL_REMOTE_HOST               remote host to connect, default=none"
		puts "   EL_CMD_FILE                  name of expect-lite script"
		puts "   EL_USER_DIR                  change to this dir upon login"
		puts "   EL_CONNECT_METHOD            telnet|ssh|ssh_key|none"
		puts "   EL_CONNECT_USER              username for telnet|ssh"
		puts "   EL_CONNECT_PASS              password for telnet|ssh"
		puts "   EL_DELAY_WAIT_FOR_HOST       default=100"
		puts "   EL_*                         user defined constants"
		puts " "
		puts "   Version $version by Craig Miller"
		exit 0
	}


	#
	#	Initializes Library function of expect-lite
	#	Pass in values for Constants and Directives (cli_str)
	#	Set library_mode, and connect to bash
	#
	proc _el_init_library { {cli_str ""} } {

		# script will core dump if libexpect is wrong version
		#puts "tclsh version:[info patchlevel]|[info tclversion]"

		if { [ catch {[package present Expect] } error ]} {
			#puts $error
			if { [info tclversion ] != "8.5" } {
				# check tclsh version before loading libexpect 
				puts "ERROR:tclsh version mismatch with libexpect, expected version 8.5, using tclversion:[info tclversion ]" 
				exit 1
			}
			package require Expect
		} 

		# test expect extensions
		if {[ catch { send_user "libexpect loaded\n"}  error ]} {
			puts "ERROR: libexpect not loaded \n$error\n"
			exit 1
		}

		# load el globals
		_el_init_globals

		# set el library mode
		set ::EL_LIBRARY 1

		# Read arguments to el_lib and call el_read_args
		# count words on cli_str
		set argc [llength [split $cli_str " "]]
		_el_read_args $argc $cli_str

		# spawn to bash for now
		#_el_connect_localhost ""
	}


	#
	#	Imports expect-lite constants into expect-lite
	#	Pass in values for Constants and Directives (cli_str)
	#	Can be called multiple times, values are additive
	#
	proc _el_import_const { {cli_str ""} } {

		# Read arguments to el_lib and call el_read_args
		# count words on cli_str
		set argc [llength [split $cli_str " "]]
		_el_read_args $argc $cli_str

	}


	#
	#	Takes full command path+file, and returns just the file name minus path
	#	
	#
	proc parse_cmd_file_nopath { cmd_file } {
		# Is there a path component?
		if {[regexp {/} $cmd_file] == 1 } {
			set temp_list [split $cmd_file '/']
			set len [expr [llength $temp_list] - 1 ]
			return [lindex $temp_list $len]
		} else {
			return $cmd_file
		}
	}

	#
	#	Assign cli arguments to the variable names
	#	Requires that $remote_host and $cmd_file are passed in via cli or
	#	that shell enviroment vars be set prior to running
	#		EL_REMOTE_HOST
	#		EL_CMD_FILE
	#		EL_USER_DIR
	#		EL_CONNECT_METHOD
	#		EL_CONNECT_USER
	#		EL_CONNECT_PASS
	#		EL_DELAY_WAIT_FOR_HOST
	#
	proc _el_read_args { argc argv } {
		global env
		upvar expectlite::remote_host			remote_host
		upvar expectlite::cmd_file				cmd_file
		upvar expectlite::user_dir				user_dir
		upvar expectlite::connect_method		connect_method
		upvar expectlite::user					user
		upvar expectlite::pass					pass
		upvar expectlite::delay_wait_for_host 	delay_wait_for_host
		upvar expectlite::cli_namespace			cli_namespace
		upvar expectlite::timeout_multiplier	timeout_multiplier
		upvar expectlite::arg0					arg0
		upvar expectlite::WARN					WARN
		upvar expectlite::DEBUG					DEBUG
		upvar expectlite::EXP_INFO				EXP_INFO
		upvar expectlite::_el_warn_color		_el_warn_color
		upvar expectlite::_el_err_color			_el_err_color

		# default timeout_multiplier multiplier to 1
		set cli_namespace($timeout_multiplier) 1

		# If needed read parameters from Env Vars
		catch { set remote_host $env(EL_REMOTE_HOST)}
		if { $remote_host == "" } { set remote_host "none" }

		if { $cmd_file=="" } { catch { set cmd_file $env(EL_CMD_FILE)} }
		if { $user_dir=="" } { catch { set user_dir $env(EL_USER_DIR)} }

		if { $user=="" } { catch { set user $env(EL_CONNECT_USER)} }
		if { $pass=="" } { catch { set pass $env(EL_CONNECT_PASS)} }
		if { [info exists env(EL_CONNECT_METHOD)] } { catch { set connect_method $env(EL_CONNECT_METHOD)} }
		if { [regexp {none|telnet|ssh|ssh_key} $connect_method] == 0 } { set connect_method "none" }

		if { [info exists env(EL_DELAY_WAIT_FOR_HOST)] } { catch { set delay_wait_for_host $env(EL_DELAY_WAIT_FOR_HOST)} }
		if { $delay_wait_for_host == "" } { set delay_wait_for_host "100" }

		# ensure arg list is separated by spaces
		set argvstr [ join $argv ]
		set argv [split $argvstr ]
		set argc [ llength $argv ]

		if { $argc >= 1 ||  $cmd_file!="" }  {
			# Walk thru additional args (should be cli constants)
			for { set x  0 } { $x < $argc} { incr x } {
				set optarg [ lindex $argv $x ]
				catch {set optargval [ lindex $argv [expr $x + 1]]}
				if { $optargval == "" } { 
					# no optargval, set to a sane value
					set optargval "--"
				}
				#puts "=====>$x|$optarg|$optargval|"
				# Check that last parameter is command file name
				# script name cannot have '=', which would be interpretted as a constant
				if {  [regexp {^[-*]|[=]}  $optarg ] == 0 && [regexp {^[-*]|[=]} $optargval] == 1 } {
					#puts ">>>$optarg>>>$optargval"
					set cmd_file $optarg
					set cli_namespace($arg0) [parse_cmd_file_nopath  $cmd_file] 
				}
				# look for help & debug options which overide script options
				# added -r and -c for remote_host & cmd_file respectively
				if { [catch { switch -glob -- $optarg {
					-h		-
					--help	{ usage }
					-v		{set WARN 10; set INFO 10}
					-vv		-
					--verbose	{set WARN 10; set DEBUG 10}
					-vvv		{set WARN 0; set DEBUG 10}
					-V		-
					--verify	{set EXP_INFO 10; set INFO 10}
					-r		{ set remote_host $optargval; incr x  }
					-c		{ set cmd_file $optargval; set cli_namespace($arg0) [parse_cmd_file_nopath  $cmd_file] ; incr x }
					-d		{ set user_dir $optargval; incr x  }
					-u		{ set user $optargval; incr x  }
					-p		{ set pass $optargval; incr x }
				} } \
				error ] } {
					puts "Warning: Bad input option: $error"
				}
				#general vars
				if [ regexp {.+=.*} $optarg ] {
					# detect and parse args in format "var=value"
					set user_var [string range $optarg 0 [expr [string first "=" $optarg] -1 ]]
					set user_value [string range $optarg [expr [string first "=" $optarg] +1 ] [string length $optarg]]
					#puts "->$user_var $user_value"
					# populate required cli parameters and cli_constants
					switch -glob -- $user_var {
						remote_host -
						r 		{ set remote_host $user_value }
						cmd_file	-
						c	 	{ set cmd_file $user_value 
								# set script constant arg0 with script name
								set cli_namespace($arg0) [parse_cmd_file_nopath  $cmd_file]
							}
						user_dir	-
						d		{set user_dir $user_value }
						user_name	-
						u		{set user $user_value }
						user_password	-
						p		{set pass $user_value }
						default {
							set cli_namespace($user_var) $user_value
							puts "Const: $user_var = $cli_namespace($user_var)"
							}
					} 

				}
				#cli-based directives (in form *DIRECTIVE)
				if [ regexp {\*[A-Z_]+} $optarg] {
					_el_global_directive $optarg
				}
			}


			#puts "->$remote_host|$cmd_file|$user_dir"

			# check that require paremeters are present
			if { $remote_host=="" } { 
				cputs "Error: Missing remote_host" $_el_err_color
				usage
				}
			if { [info exists ::EL_LIBRARY] != 1 && $remote_host=="none" || $remote_host=="NONE" } { 
				if {$WARN} { cputs "Warning: Remote Host=none, using Localhost" $_el_warn_color }
				# don't use remote login method but use localhost	
				set connect_method "none"
				}
			if { $cmd_file=="" && [info exists ::EL_LIBRARY] != 1} { 
				cputs "Error: Missing cmd_file"  $_el_err_color
				usage
				}
		}

		# Assign any Environment Variables starting with 'EL_' to Constants
		# only show in Debug and not Library mode
		if { $DEBUG && [info exists ::EL_LIBRARY] != 1} {
			puts "Env Vars -> Constants:"
		}
		foreach key [array names env] {
			if {[regexp {^EL_} $key] == 1 } {
				if { $DEBUG && [info exists ::EL_LIBRARY] != 1} {
					puts "$key=$env($key)"
				}
				set cli_namespace($key) $env($key)
			}
		}

		if {[info exists ::EL_LIBRARY] != 1} {
			# show help if command file (script) is not given on command line			
			if { $argc < 1 && $cmd_file=="" }  {
				usage
			}
		}
	}


	#
	#	Takes full command path+file, and returns just the path to command file minus the filename
	#	This is used in including files
	#
	proc parse_cmd_file_path { cmd_file } {
		# Is there a path component?
		if {[regexp {/} $cmd_file] == 1 } {
			set temp_list [split $cmd_file '/']
			set len [expr [llength $temp_list] - 1 ]
			regsub [lindex $temp_list $len] $cmd_file "" temp_path

			return $temp_path
		} else {
			return ""
		}
	}

	#
	#	Login Method which uses user and password via Telnet to log into hosts
	#	If this is not your access method use one of the other login methods
	#
	proc connect_telnet_host {remote_host {optional_suppress_stdout "no"}} {
		global spawn_id 
		upvar expectlite::DEBUG					DEBUG
		upvar expectlite::_el_timeout			_el_timeout
		upvar expectlite::_el_telnet			_el_telnet
		upvar expectlite::user					user
		upvar expectlite::pass					pass
		
		
		
		#This spawns the telnet program - auto generates spawn_id var
		set timeout 5
		if { ($optional_suppress_stdout == "suppress_stdout") && ($DEBUG != 1)} {
			# turn off stdout - suppress login banners - show with -vv
			log_user 0
		}
		if { [catch { spawn $_el_telnet $remote_host  } error] } {
			puts stderr "CONNECT ERROR: Unable to connect to $remote_host \n   $error"
			# Fail script?
			exit 1
		}
		#The script expects login
		expect "ogin:"

		#The script sends the user variable
		if { [catch { send "$user\r"   } error] } {
			puts stderr "CONNECT ERROR: Unable to connect to $remote_host \n   $error"
			# Fail script?
			exit 1
		}
		#The script expects Password
		expect "assword:" 

		#The script sends the password variable
		# do not log password
		set log_user 0
		send "$pass\r" 
		set log_user 1
		#wait_for_prompt
		set timeout $_el_timeout
		# turn on stdout
		log_user 1
	}

	#
	#	Subroutine used by ssh user/password method
	#	If this is not your access method use one of the other login methods
	#
	proc zlogin { pass login_attempts } {
		upvar expectlite::remote_host		remote_host
		upvar expectlite::_el_eols			_el_eols
		
		set timeout 2
		set MAXLOGINATTEMPT 10
		#set login_attempts 0
		while { $login_attempts < $MAXLOGINATTEMPT} {
			#puts "loop: $login_attempts"
			expect {
				-re "assword:"	{
					# do not log password
					set log_user 0
					send "$pass$_el_eols" 
					set log_user 1
					#puts "send pass"
					}
				-re "Are you sure you want to continue connecting" {
					send "yes$_el_eols"
					#zlogin $pass $login_attempts
					}
				-re "Last login" { return }
				-re "Tomato" { return }
				-re {.+\n} { #expect connected  }

			 }
			 incr login_attempts
			 #puts "here"
		 }
		 if { $login_attempts >= $MAXLOGINATTEMPT} {
			puts stderr "\nCONNECT ERROR:Unable to login to $remote_host \n "
			exit 1
		 }
	}

	#
	#	Login Method which uses user and password via ssh to log into hosts
	#	If this is not your access method use one of the other login methods
	#
	proc connect_ssh_host {remote_host {optional_suppress_stdout "no"}} {
		global spawn_id env
		upvar expectlite::user					user
		upvar expectlite::pass					pass
		upvar expectlite::_el_timeout			_el_timeout
		upvar expectlite::DEBUG					DEBUG
		
		
		# work around for older versions of expect
		#This spawns the ssh program - auto generates spawn_id var
		#spawn ssh -l $user $remote_host
		set timeout 15
		if { $user == "" } {
			# user username from environment
			set user $env(USER)
		}
		# suppress stdout during login
		if { ($optional_suppress_stdout == "suppress_stdout") && ($DEBUG != 1)} {
			# turn off stdout - suppress login banners - show with -vv
			log_user 0
		}
		if { [catch { spawn ssh -l $user $remote_host  } error] } {
			puts stderr "CONNECT ERROR: Unable to connect to $remote_host \n   $error"
			# Fail script?
			exit 1
		}
		# debug help
		#exp_internal 1

		set login_attempt 0
		#better way of logging in (and catch any errors)
		if { [ catch {zlogin $pass $login_attempt} error] } {
			puts stderr "CONNECTION ERROR to $remote_host \n   $error"
			exit 1
		}
		# turn off the debugging
		#exp_internal 0
		set timeout $_el_timeout
		# turn on stdout
		log_user 1

	}

	#
	#	Login Method which assumes ssh keys are configured for login hosts
	#	If this is not your access method use one of the other login methods
	#
	proc connect_ssh_key_host {remote_host {optional_suppress_stdout "no"}} {
		global spawn_id timeout
		upvar expectlite::user					user
		upvar expectlite::pass					pass
		upvar expectlite::DEBUG					DEBUG
		upvar expectlite::delay_wait_for_host	delay_wait_for_host
		upvar expectlite::_el_eols				_el_eols
		upvar expectlite::_el_timeout				_el_timeout
		
		
		# work around for older versions of expect
		# Enable use of an alternet user for ssh_key method
		# if $user is not blank, then use the alternet user
		set timeout 30
		if { $user != "" } { 
			set login_user "$user@"
		} else {
			set login_user ""
		}
		# suppress stdout during login
		if { ($optional_suppress_stdout == "suppress_stdout") && ($DEBUG != 1)} {
			# turn off stdout - suppress login banners - show with -vv
			log_user 0
		}
		#This spawns the ssh program - auto generates spawn_id var
		if { [catch { spawn ssh $login_user$remote_host  } error] } {
			puts stderr "CONNECT ERROR: Unable to connect to $remote_host \n   $error"
			# Fail script?
			exit 1
		}
		# TODO:Auto update ~/.ssh/known_hosts if needed

		# Test the connection by sending a return
		if { [catch { send "$_el_eols"   } error] } {
			puts stderr "CONNECT ERROR: Unable to connect to $remote_host \n   $error"
			# Fail script?
			exit 1
		}
		# allow time for remote banner (if present) to echo
		#after [expr $delay_wait_for_host * 2]

		# answer quetion if given, else take any response as "connected"
		set timeout 5
		expect {
			-re "Are you sure you want to continue connecting" {
			send "yes$_el_eols"
			}
			-re {.+} { #puts "expect-lite: Connected" }
			timeout { }
		}
		# debug help
		#exp_internal 1

		# turn off the debugging
		#exp_internal 0
		set timeout $_el_timeout
		# turn on stdout
		log_user 1

	}

	#
	#	Login Method connects to localhost (no telnet or ssh)
	#	If this is not your access method use one of the other login methods
	#
	proc _el_connect_localhost {remote_host} {
		# this proc just spawns a connection to a shell
		# all other functions are handled by remote_host_init

		global  spawn_id 
		upvar expectlite::remote_shell			remote_shell
		upvar expectlite::_el_eols				_el_eols
		
		
		# protect against empty remote_shell
		if { $remote_shell == "" } { set remote_shell "bash" }
		# spawn a local shell for localhost
		if { [catch { spawn $remote_shell  } error] } {
			puts stderr "CONNECT ERROR: Unable to connect to localhost \n   $error"
			# Fail script?
			exit 1
		}
		send " $_el_eols"

	}

	#
	#	Reads configuration directives, prior to command file execution
	#	Sets global variables
	#	This function will eventually replace all user defined globals at top of script
	#
	proc read_set_config_directives { buf_stack } {
		upvar expectlite::remote_shell			remote_shell
		upvar expectlite::_el_buf				_el_buf

		# search for shell configuration directive (**SHELL=...)
		set _search "^\\\*\*SHELL=.*"

		# search buffer _el_buf($buf_stack) with $_search  
		set _buf_ptr [lsearch -regexp $_el_buf($buf_stack) $_search ]

		set _line [_el_buffer_read $_buf_ptr $buf_stack]	
		#puts "shell_line-------->$_line|$_buf_ptr"
		if { $_buf_ptr != -1 } {
			set _line_list [split $_line "="]
			if { [llength $_line_list ] == 2 } {
				set remote_shell [lindex $_line_list 1]
			}
			puts "expect-lite directive: Shell is: $remote_shell"
		}
	}

	#
	#	Wraps expect send command in catch, reports error if connection is lost
	#	Error condition terminates script
	#
	proc protected_send { str } {
		upvar expectlite::remote_host			remote_host

		if { [catch { send "$str"   } error] } {
			puts stderr "CONNECT ERROR: Unable to connect to remote_host: $remote_host \n   $error"
			# Fail script?
			exit 1
		}
	}

	#
	#	After logged into remote host, does house keeping activities before starting expect-lite script
	#		starts shell on remote host, sets sane prompt, and cd's to user directory (aka user_dir) if needed
	#	
	proc remote_host_init { user_dir  } {
		global spawn_id timeout
		upvar expectlite::remote_host			remote_host
		upvar expectlite::remote_shell			remote_shell
#		upvar expectlite::PROMPT				PROMPT
		upvar expectlite::DEBUG					DEBUG
		upvar expectlite::_el_timeout			_el_timeout
		upvar expectlite::connect_method		connect_method
		upvar expectlite::delay_wait_for_host	delay_wait_for_host
		upvar expectlite::_el_eols				_el_eols

		set timeout 2

		# suppress stdout during login for *FORK
		if { $DEBUG != 1} {
			# turn off stdout - suppress login banners - show with -vv
			log_user 0
		}

		# ensure we are using user's desired shell, and check connection
		if {$connect_method != "none" } {
			# handle real remote host invocation of shell
			protected_send "$remote_shell$_el_eols"

		}
		# wait for echo to appear when starting a shell
		if { $remote_shell != "" } {
			expect {
				-re ".+\[%>#$\] $remote_shell.+" { if {$DEBUG} { puts "DEBUG: Starting Shell: $remote_shell\n" } }
				timeout { if {$DEBUG} { puts "DEBUG: Unable to detect Shell Start\n" } }
			}
		}
		# set sane prompt
		switch -glob -- $remote_shell {
			csh -
			tcsh 	{ set _prompt "%c02 %# %L"
						protected_send "set prompt = \"$_prompt\"$_el_eols" 
						expect -re {.+prompt.+} { }
					}
			sh -
			ksh -
			bash		{ set _prompt "\"\[\\u@\\h:\\w\]\\$ \"" 
						protected_send "export PS1=$_prompt$_el_eols" 
						expect -re {.+PS1.+} { }
					}
			default 	{ set _prompt "\$ " 
						protected_send "export PS1=\'$_prompt\'$_el_eols" 
						protected_send "set prompt = \"$_prompt\"$_el_eols" }
		}
		# clear expect_out(buffer)
		#expect -re {.+} {}

		# turn on stdout
		log_user 1

		#only change directory if user_dir was passed as an arg, otherwise, user will set it in expect-lite script
		if { $user_dir != "" } {
			protected_send "cd $user_dir$_el_eols"
			wait_for_prompt
		}
		set timeout $_el_timeout
	}

	#
	#	Control spawn_id sessions on host
	#
	#
	proc session_id_manager { fork_id } {
		global spawn_id timeout
		upvar expectlite::_el				_el
		upvar expectlite::_el_fork			_el_fork
		upvar expectlite::_el_fork_id_list	_el_fork_id_list
		upvar expectlite::INFO				INFO
		upvar expectlite::connect_method	connect_method
		upvar expectlite::remote_host		remote_host
		upvar expectlite::DEBUG 			DEBUG
		upvar expectlite::user_dir			user_dir
		upvar expectlite::_el_info_color	_el_info_color
		upvar expectlite::_el_warn_color	_el_warn_color
		upvar expectlite::_el_eols			_el_eols

		
		if { $fork_id == "default" } {
			# set fork session back to default if count > 0
			if { $_el(fork_count) > 0 } {
				set spawn_id  $_el_fork(default,session)
			}
		} elseif { $fork_id != "" } {
			# request for new fork session

			if {[info exists _el_fork($fork_id,session) ]} {
				# connect to existing session
				set spawn_id $_el_fork($fork_id,session)
			} else {
				# save current timeout
				set current_timeout $timeout
				# start new session on remote_host
				# Use selected (at top of this file) connect_method
				switch $connect_method {
					telnet	{connect_telnet_host $remote_host "suppress_stdout" }
					ssh		{connect_ssh_host 	$remote_host "suppress_stdout" }
					ssh_key	{connect_ssh_key_host $remote_host "suppress_stdout"}
					none		{_el_connect_localhost $remote_host }
				}
				# set a sane prompt for additional sessions
				remote_host_init $user_dir
				# restore current timeout
				set timeout $current_timeout 
				set _el_fork($fork_id,session) $spawn_id
				incr _el(fork_count)
				# update list of fork_id's
				lappend _el_fork_id_list $fork_id
				# set default eols for this session
				set _el_fork($fork_id,eols) $_el_eols
			}
			# end if		
		} elseif {$fork_id == "" } {
			# Show current session name
			if { [info exists _el(fork_current)] } {
				set fork_id $_el(fork_current)
			} else {
				set fork_id "default"
			}
		}
		# clear and regenerate fork id list and count
		set _el_fork_id_list { }
		set _el(fork_count) 0
		foreach keyitem [array names _el_fork] {
			#filter out eols from session printout
			if { [string match "*,session" $keyitem] } {
				# retrieve session name, drop ',session' from keyitem
				set session_name [lindex [split $keyitem ,] 0]
				lappend _el_fork_id_list $session_name
				incr _el(fork_count)
				#puts "=====>$keyitem|$session_name|$_el_fork_id_list|$_el(fork_count)"
			}
		}

		if {$INFO && !$DEBUG} {
			cputs "\n\nINFO: FORK session is: $fork_id,	Active sessions are: $_el_fork_id_list\n" $_el_info_color
		} elseif { $DEBUG } {
			puts "\n\nDEBUG: FORK session is: $fork_id, Spawn_id=$spawn_id, fork_count:$_el(fork_count), active sessions:$_el_fork_id_list\n"
		}
		set _el(fork_current) $fork_id
		return $fork_id
	}

	#
	#	el_lib: import session names and spawn_ids
	#	Enables external app to spawn sessions, and feed list to EL
	#
	proc _el_import_session_ids { session_list } {
		global spawn_id 
		upvar expectlite::_el				_el
		upvar expectlite::_el_fork			_el_fork
		upvar expectlite::_el_fork_id_list	_el_fork_id_list
		upvar expectlite::INFO				INFO
		upvar expectlite::DEBUG 			DEBUG
		upvar expectlite::_el_info_color	_el_info_color
		upvar expectlite::_el_err_color		_el_err_color
		upvar expectlite::_el_eols			_el_eols

		puts  "SL>$session_list|[llength $session_list]"
		if { [ catch { array set import_fork  $session_list} error ]} {
			cputs "ERROR: Invalid array:$session_list:\n$error\n" $_el_err_color
		}

		# set current fork_id to current spawn_id
		foreach keyitem [array names import_fork] {
			#filter out eols from keyitems
			puts "SL>$keyitem"
			# set current fork_id to current spawn_id
			puts "SL2>$keyitem"
			if { [info exists spawn_id ] && $import_fork($keyitem,session) == $spawn_id } {
				set _el(fork_current) $keyitem
				set _el_fork(fork_current,eols) $_el_eols
			}
			# set default eol context 
			set _el_fork($keyitem,eols) $_el_eols
		}
		# set spawn_id to default, if not currently set
		if { [info exists spawn_id ] != 1 && [ info exists import_fork(default)] } {
			set spawn_id $import_fork(default)
		} else {
			cputs "ERROR: Import Sessions: no 'default' session found\n    Sessions imported: [array names import_fork]" $_el_err_color 
			exit 1
		}
		# convert imported session array to el_fork array
		foreach keyitem [array names import_fork] {
			set _el_fork($keyitem,session) $import_fork($keyitem)
		}

		# show array
		if { $DEBUG } {
			puts "Printing session ids:"
			foreach keyitem [array names _el_fork] {
				#filter out eols from session printout
				if { [string match "*session*" $keyitem] == 1 } {
					set session_name [lindex [split $keyitem ,] 0]
					puts "	$session_name=$_el_fork($keyitem)"
				}
			}
		}; #endif DEBUG
	}


	#
	#	push spawn_id sessions on fork_id stack
	#	used by interact
	#
	proc push_session_id { fork_id } {
		global spawn_id 
		upvar expectlite::_el				_el
		upvar expectlite::_el_fork			_el_fork
		upvar expectlite::_el_fork_stack	_el_fork_stack
		upvar expectlite::INFO				INFO
		upvar expectlite::DEBUG 			DEBUG

		# remove any \n or \r's
		regsub -all {[ \n\r]+} $fork_id "" fork_id
		if { [string length $fork_id ] > 0 } {
			# push new session_id on stack
			lappend _el_fork_stack $_el(fork_current)
		}
		set len [llength $_el_fork_stack]
	}

	#
	#	pop spawn_id sessions from fork_id stack
	#	used by interact, to set proper *FORK session when continuing the expect-lite script
	#
	proc pop_session_id { } {
		global spawn_id 
		upvar expectlite::_el				_el
		upvar expectlite::_el_fork			_el_fork
		upvar expectlite::_el_fork_stack	_el_fork_stack
		upvar expectlite::INFO				INFO
		upvar expectlite::DEBUG 			DEBUG

		if {[info exists _el_fork_stack ]} {
			set len [llength $_el_fork_stack]
			if { $len > 0 } {
				# get last value on stack
				set fork_id [lindex $_el_fork_stack [expr $len -1 ]]
				# delete last item in stack
				set _el_fork_stack [lreplace $_el_fork_stack end end]
				# return last value of fork_id from stack
				return $fork_id
			}	
		}
		return ""
	}

	#
	#	Subroutine to remove first char from string and return substring
	#
	#
	proc _el_strip_char { string char } {
		#puts "s->$string||$char||"

		regsub -- $char $string "" stringb
		#puts "s->$stringb||$char||"
		return $stringb
	}



	#
	#	Find a color in a comment line
	#	
	#
	proc find_color { str } {
		upvar expectlite::el_color		el_color

		set fcolor ""
		# detect first word of comment, if it is a color, then so use it
		set l_str [split $str]
		set first_word [lindex $l_str 0 ]
		#puts "------------------>$l_str|$first_word|[lsearch [array names el_color] $first_word ]|"
		if {[lsearch [array names el_color] $first_word ] != -1} {
			set fcolor $first_word
		}
		return $fcolor
	}

	#
	#	Prints a comment line (begins with a ';')
	#	
	#
	proc print_comment_line { } {
		upvar expectlite::el_color			el_color
		upvar expectlite::_el_info_color	_el_info_color
		upvar expectlite::_el_comment_color	_el_comment_color
		upvar expectlite::timeout_line		timeout_line
		upvar expectlite::comment_line		comment_line


		# initialize font color
		if { $comment_line != "" } {
			set fcolor [find_color $comment_line]
			if { $fcolor != "" } {
				# remove first word
				regsub "$fcolor" $comment_line "" comment_line
			}
		}

		# print ';' printable comment
		if { $comment_line != "" } {
			#puts "\n$comment_line\n"
			cputs "\n$comment_line\n" $fcolor
			set comment_line ""
			} 
		# print new expect timeout value (if needed)
		if { $timeout_line != "" } {
			cputs "\n$timeout_line\n" $_el_info_color
			set timeout_line ""
			} 

	}


	#
	#	Prints all user vars and constants
	#	Called by *SHOW VARS & IDE <esc>v
	#
	proc show_vars { } {
		upvar expectlite::user_namespace		user_namespace
		upvar expectlite::cli_namespace			cli_namespace
		upvar expectlite::arg0					arg0

		set blank "                    "
		set blank_len [string length $blank]
		set var_list [array names user_namespace]
		set var_list_sorted [lsort $var_list]

		puts "DEBUG Info: Printing all expect-lite variables"
		# print internal vars
		if { [ info exists cli_namespace($arg0) ] } { 
			set ii_len [string length $arg0]
			set ii_blank [string range $blank 1 [expr $blank_len - $ii_len]]
			puts "Var:arg0 $ii_blank Value:$cli_namespace($arg0)"
		}
		foreach ii $var_list_sorted {
			# create aligned columns
			set ii_len [string length $ii]
			set ii_blank [string range $blank 1 [expr $blank_len - $ii_len]]
			# is a constant overiding the var?
			if { [info exists cli_namespace($ii) ] } {
				set iii_len [string length $cli_namespace($ii)]
				set iii_blank [string range $blank 1 [expr $blank_len - $iii_len]]
			}
			# don't print array initialization var (zero)
			if { $ii != 0 } {
				if { [info exists cli_namespace($ii) ] } { 
					# print var, value, Const
					puts "Var:$ii $ii_blank Value:$user_namespace($ii) $iii_blank Const:$cli_namespace($ii)"
				} else {
					# print var, value
					puts "Var:$ii $ii_blank Value:$user_namespace($ii)"

				}
			}
		}
	}

	#
	#	Resolves user vars to values, only if there isn't a Constant with the same name
	#	Constants are always given preference over user vars (defined in the script)
	#	returns value of var if known, or replaces $var format (for shell vars)
	#
	proc resolve_var_sub var {
		upvar expectlite::user_namespace		user_namespace
		upvar expectlite::cli_namespace			cli_namespace
		upvar expectlite::_el_info_color		_el_info_color
		upvar expectlite::INFO					INFO
		upvar expectlite::WARN					WARN
		upvar expectlite::DEBUG					DEBUG

		if { $var != "" } {
			set append_value 1
			# we have the var
			set user_var $var

			#puts "var>$user_var"

			# Give priority over Constants, only assign Vars if Constant not present
			if { [catch {set user_value $cli_namespace($user_var)}]  } {
				if { [catch {set user_value $user_namespace($user_var)}] } {
					# go quietly into the night
					#puts stderr "VAR NOT FOUND: |$user_var|"
					# unknown var
					set append_value 0
				}

			} else {
				# Show all constant substitutions if in DEBUG
				if {$DEBUG} {
					cputs  "INFO: CONST FOUND: $user_var = $cli_namespace($user_var)" $_el_info_color
				}
				# Show non ENV constant substitutions in INFO
				if { $INFO==1 && [regexp {^EL_} $user_var]!=1 } {

					cputs  "INFO: CONST FOUND: $user_var = $cli_namespace($user_var)" $_el_info_color
				}
			}
			if { $append_value } {
				#puts "u_v>$var=$user_value"
				# only append valid vars
				return $user_value
			} else {
				# if unknown var, be sure to put it back on line with a '$' in front for shell vars
				return "\$$var"
			}
		} else {
			return ""
		}

	}

	#
	#	A rather complicated subroutine (sorry) which parses out user vars in the script
	#	The sep_list defines what chars are _not_ in vars
	#	returns value of var, or $var if shell var
	#
	#	This function uses a two stage TCL command "split" to parse the expect-lite variables
	#		For example, the following line requires parsing: "mount$this$that#other"
	#			1) The first stage split will split on '$' resulting in "mount this that#other"
	#			2) "mount" will be discarded, since it is the first part, and not preceeded with a '$'
	#			3) The remaining line segments (in a TCL list) will go to the second split stage which
	#				cycles thru a known list of characters signaling the end of a variable name (sep_list)
	#			4) "this" passes the second stage, and is passed to proc resolve_var_sub for resolution
	#			5) "that#other" will be split again into "that other" in the second split stage
	#			6) Only the first line segment after the second stage split is sent to proc resolve_var_sub 
	#				for resolution (of key value pairs)
	#				In this example it would be "that" which is sent for resolution
	#			7) The entire line is reassembled after var resolution, and returned
	#
	proc parse_var { var sep } {
		# parses the string passed, separating out the Vars and passing them to resolve_var_sub to be resolved
		# List of Chars which define the end of a Var
		set sep_list { ! @ # % * / { } \\ ( ) . [ ] = ? \" : - , | }
		#puts "v>$var|s>$sep"
		set resolved_line ""

		set var_list [ split $var $sep]
		#puts "vl>$var_list|sep>$sep"
		# If only 1 var on the line
		if { [llength $var_list] == 1 } {
			if { $sep == "$" } {
				#puts "one only"
				# return first chunk which is NOT a var in var_list
				append resolved_line $var
			} else {

				append resolved_line [resolve_var_sub $var]
			}
		} else {
			# First time in (no recursion) 
			if { $sep == "$" } {
				append resolved_line [lindex $var_list 0]
			} else {
				# Dereferences Vars on First time through the var_list
				#puts "look here"
				append resolved_line [resolve_var_sub [lindex $var_list 0]]
			}
			#puts "vl>$var_list"
			for {set j 1} {$j<[llength $var_list]} {incr j} {
				set var_only 1
				# are there other strange chars in the var?
				# use low_water_marker to find closest one
				# FIXME: limits var length to less that 255
				set low_water_marker 255
				foreach k $sep_list {
					set odd_char_pos [string first $k [lindex $var_list $j]]
					if { $odd_char_pos > -1 } { 
						if { $odd_char_pos < $low_water_marker } { 
							set low_water_marker $odd_char_pos
							set s $k
						}
						set var_only 0
					}
					#puts "k>[string first $k [lindex $var_list $j]]]"
				}
				if { $var_only } {
					# "var only" no other strange chars found
					if { $sep == "$" } {
						#puts "vo>[lindex $var_list $j]|j>$j"
						append resolved_line [resolve_var_sub [lindex $var_list $j]]
					} else {
						#puts "j>$j|sep>$sep"
						# add literals back to the line (in a recursed call)
						append resolved_line $sep[lindex $var_list $j]
					}
				} else {
					#puts "j>$j|recurse!"
					# Recursion Call with smaller chunck - splits known var with "extra" chars
					# and make sure the var length isn't zero
					if { ($sep == "$") && ($low_water_marker != 0) } {
						append resolved_line [parse_var [lindex $var_list $j] $s]
					} else {
						append resolved_line $sep[lindex $var_list $j]
					}
				}
				#puts "j>$j|val>$resolved_line"
			}
		}
		#puts "r>$resolved_line"
		return $resolved_line
	} 
	# end of parse_var



	#
	#	Buffer management functions
	#	A buffer is used to permit random access to the command script
	#		This enables looping, but setting the buffer pointer to a 
	#		previous line in the script
	#	The conditional/jump mechanism will use this method for looping
	#

	#
	#	Opens command script and reads into buffer (tcl list)
	#	Returns buffer
	#
	proc _el_buffer {cmd_file} {
		upvar expectlite::_el_buf					_el_buf

		set line_buffer {}
		# Protect against no file to read
		if { [ catch { set infile [open $cmd_file r ] } error] } {
			puts stderr "Unable to open cmd/include_file \n   $error"
				exit 1
		}
		while { -1 != [gets $infile buf_line ]} {
			# remove leading white space on line (permitting indentation
			regsub {^[ \t]+} $buf_line {} buf_line

			#trim white space at end of labels
			if { [ regexp {^%} $buf_line ] } {
				# remove leading white space on line (permitting indentation
				regsub {[ \t]+$} $buf_line {} buf_line
			}

			# don't read in blank lines - make sure to include all needed lines [><?+*-~;@$=!%]
			if { [regexp {^[$-/:-@!~]} $buf_line ] } {
				lappend line_buffer $buf_line
			}
		}
		close $infile
		#puts "-> $line_buffer|[llength $line_buffer] "

		# push new buffer stack
		incr _el_buf(stack)
		# save the buffer
		set _el_buf($_el_buf(stack)) $line_buffer
		# initialize the buffer pointer - points to the line no in the buffer
		set _el_buf($_el_buf(stack),ptr) 0

		# return the stack ID of this buffer
		return $_el_buf(stack)
	}

	#
	#	Reads buffer as if it were a file, returning a line from the buffer
	#	
	#
	proc _el_buffer_read { line_no buf_stack} {
		upvar expectlite::_el_buf					_el_buf

		#puts " $line_no | $buf_stack | $_el_buf($buf_stack,ptr) "

		# Do not read past end of buffer
		if { $line_no < [llength $_el_buf($buf_stack)] } {
			return [lindex $_el_buf($buf_stack) $line_no]
		} else {
			return -1
		}
	}

	#
	#	Reads buffer, prints n lines
	#	
	#
	proc _el_buffer_show { num_of_lines buf_stack} {
		upvar expectlite::_el_buf				_el_buf
		upvar expectlite::_el_info_color		_el_info_color


		#puts " $num_of_lines | $buf_stack | $_el_buf($buf_stack,ptr) | [expr  $_el_buf($buf_stack,ptr) + $num_of_lines] "

		# don't print lines which are not in the buffer (have -1)
		proc print_line { line } {
			#global _el_info_color
			upvar  expectlite::_el_info_color		_el_info_color
			
			if { $line != -1 } {
				cputs "$line" $_el_info_color
			}
		} 
		# end print_line

		if {$num_of_lines > 0} {
			cputs "Printing next $num_of_lines line(s) of script:" $_el_info_color
			set num_of_lines [expr $num_of_lines + 1 ]
			for {set n [expr $_el_buf($buf_stack,ptr) +1]} {$n<[expr $num_of_lines + $_el_buf($buf_stack,ptr)]} {incr n} {
				print_line [ _el_buffer_read $n $buf_stack ]
			}
		} elseif {$num_of_lines < 0} {
			cputs "Printing last [expr {abs($num_of_lines)}] line(s) of script:" $_el_info_color
			for {set n [expr  $_el_buf($buf_stack,ptr) + $num_of_lines]  } {$n <  $_el_buf($buf_stack,ptr)} {incr n } {
				print_line [ _el_buffer_read $n $buf_stack ]
			}
		} else {
			# print current line
			cputs "Printing current line of script:" $_el_info_color
				print_line [ _el_buffer_read $_el_buf($buf_stack,ptr) $buf_stack ]

		}
	}

	#
	#	Buffer Search
	#	Searches buffer from line_ptr down to end of buffer
	#	If search fails, search will 'wrap' and continue from beginning of buffer
	#	This allows duplicate search items in the buffer, finding the first succeding one
	#		However search items used as looping labels should be unique!
	#
	proc _el_buffer_search2 {line_ptr line_search buf_stack} {
		upvar expectlite::_el_infinite_loop		_el_infinite_loop
		upvar expectlite::el_color				el_color
		upvar expectlite::INFO					INFO
		upvar expectlite::WARN					WARN
		upvar expectlite::DEBUG					DEBUG
		upvar expectlite::EXP_INFO				EXP_INFO
		upvar expectlite::_el_info_color		_el_info_color
		upvar expectlite::_el_err_color			_el_err_color
		upvar expectlite::_el_comment_color		_el_comment_color
		upvar expectlite::_el_buf				_el_buf



		# search down first
		if {$INFO} {
			# start INFO color
			if {$_el_comment_color == "none"} {
				puts  -nonewline $el_color($_el_info_color)
			}
			puts -nonewline "\nJumping to label"

		}
		set _ptr [lsearch -start $line_ptr $_el_buf($buf_stack) $line_search]
		if { $_ptr == -1 } {
			# wrap search starting at beginning of buffer
			if {$INFO} {
				puts -nonewline ", Wrapping search"
			}
			set _ptr [lsearch  $_el_buf($buf_stack) $line_search]
			if {$WARN && $_ptr < 0} {
				puts -nonewline "\n\nWARNING: Label not found"
			}

			# decrement infinite loop detector 
			incr _el_infinite_loop -1
			if { $_el_infinite_loop < 1 } {
				# return search not found
				cputs ":$line_search\n\n\nERROR: Infinite Loop Detected!\n\n" $_el_err_color
				return -2
			}
		}
		# put final field on info line
		if {$INFO} {
			cputs ":$line_search\n" $_el_info_color
		}
		#puts "|$line_search|$line_ptr|$_ptr|[lindex $_el_buf($buf_stack) 209]|"
		return $_ptr
	}
	#
	#	End of Buffer management functions
	#

	#
	#	Buffer Search
	#	Searches buffer from line_ptr down to end of buffer
	#	If search fails, search will 'wrap' and continue from beginning of buffer
	#	This allows duplicate search items in the buffer, finding the first succeding one
	#		However search items used as looping labels should be unique!
	#
	proc _el_buffer_search {line_ptr line_search buf_stack} {
		upvar expectlite::_el_infinite_loop		_el_infinite_loop
		upvar expectlite::el_color				el_color
		upvar expectlite::INFO					INFO
		upvar expectlite::WARN					WARN
		upvar expectlite::DEBUG					DEBUG
		upvar expectlite::EXP_INFO				EXP_INFO
		upvar expectlite::_el_info_color		_el_info_color
		upvar expectlite::_el_err_color			_el_err_color
		upvar expectlite::_el_comment_color		_el_comment_color
		upvar expectlite::_el_buf				_el_buf



		# search down first
		if {$INFO} {
			# start INFO color
			if {$_el_comment_color == "none"} {
				puts  -nonewline $el_color($_el_info_color)
			}
			puts -nonewline "\nJumping to label"

		}
		# get a list of pointers which match search
		set _ptr_list [lsearch -all $_el_buf($buf_stack) $line_search]
		
		if { $_ptr_list == {} } {
			if {$WARN} {
				puts -nonewline "\n\nWARNING: Label not found"
			}
			# signal label not found
			set _ptr -1
		} else {
			# give preference to search backwards
			set i -1
			foreach ptr $_ptr_list {
				if { $ptr < $line_ptr } { incr i }
			}
			if { $i == -1} {
				# not found then search forwards
				foreach ptr $_ptr_list {
					if { $ptr > $line_ptr } { incr i ; break }
				}
			}
			set _ptr [lindex $_ptr_list $i]
			# decrement infinite loop detector 
			incr _el_infinite_loop -1
			if { $_el_infinite_loop < 1 } {
				# return search not found
				cputs ":$line_search\n\n\nERROR: Infinite Loop Detected!\n\n" $_el_err_color
				return -2
			}
		}
		
		# put final field on info line
		if {$INFO} {
			cputs ":$line_search\n" $_el_info_color
		}
		#puts "|$line_search|$line_ptr|$_ptr_list|$_ptr|[lindex $_el_buf($buf_stack) 152]|"
		return $_ptr
	}
	#
	#	End of Buffer management functions
	#

	#
	#	Set Global expect-lite Directives
	#	
	#
	proc _el_global_directive { g_dir_str } {
		global timeout
		upvar expectlite::INFO					INFO
		upvar expectlite::EXP_INFO				EXP_INFO
		upvar expectlite::WARN					WARN
		upvar expectlite::DEBUG 				DEBUG
		upvar expectlite::_el_info_color		_el_info_color
		upvar expectlite::_el_warn_color		_el_warn_color
		upvar expectlite::_el_err_color 		_el_err_color
		upvar expectlite::_el_comment_color 	_el_comment_color
		upvar expectlite::_el_eols				_el_eols
		upvar expectlite::_el_fork				_el_fork
		upvar expectlite::_el_current_session	_el_current_session
		upvar expectlite::_el_infinite_loop 	_el_infinite_loop
		upvar expectlite::NOFAIL				NOFAIL
		upvar expectlite::_el					_el
		upvar expectlite::TIMESTAMP 			TIMESTAMP



		switch -glob $g_dir_str {	
			"\\*INFO"	{	# Turn on INFO logging
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				set INFO 1
			}
			"\\*EXP_INFO"	{	# Turn on Expect INFO logging
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				set EXP_INFO 1
			}
			"\\*WARN"	{	# Turn on WARN logging
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				set WARN 1
			}
			"\\*DEBUG"	{	# Turn on DEBUG logging
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				set DEBUG 1
			}
			"\\*NOINFO"	{	# Turn off INFO logging
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				# do not disable if CLI has enabled
				if {$INFO != 10 } { set INFO 0 }

			}
			"\\*NOEXP_INFO"	{	# Turn off Expect INFO logging
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				if {$EXP_INFO != 10 } { set EXP_INFO 0 }
			}
			"\\*NOWARN"	{	# Turn off WARN logging
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				# do not disable if CLI has enabled
				if {$WARN != 10 } { set WARN 0 }

			}
			"\\*NODEBUG"	{	# Turn off DEBUG logging
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				# do not disable if CLI has enabled
				if {$DEBUG != 10 } { set DEBUG 0 }

			}
			"\\*NOCOLOR"	-
			"\\*NOCOLOUR"	{	# Turn off Color logging
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				# set INFO color
				set _el_info_color none
				# set WARN color
				set _el_warn_color none
				# set ERROR color
				set _el_err_color none
				# set global color
				set _el_comment_color none		 	
			}
			"\\*NOTIMESTAMP"	{	# Turn off TIMESTAMP logging
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				# do not disable if CLI has enabled
				if {$TIMESTAMP != 10 } { set TIMESTAMP 0 }
			}
			"\\*TERM"	{	# Terminate but Pass Test
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				### Terminate the test with a pass
				if {$INFO} {cputs "\n\n##Overall Result: PASS \n\n" $_el_info_color }
				exit 0
				
			}
			"\\*FAIL"	{	# Terminate and Fail Test
				cputs "\nexpect-lite directive: $g_dir_str " $_el_err_color
				if { $NOFAIL } {
					# set test to fail
					return 0
				} else {
					# NOFAIL not set, fail right now
					return 2
				}
			}
			"\\*EOLS LF"	{	# Set End of Line Sequence to LF
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				set _el_eols "\n"
			}
			"\\*EOLS CRLF"	{	# Set End of Line Sequence to CRLF
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				set _el_eols "\r\n"
			}
			"\\*SHOW VARS"	{	# Show expect-lite variables
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				show_vars
			}
			"\\*INTERACT"	{	# place into expect interact mode
				# Skip interact if in Library mode
				# Jump to interact
				if { [info exist ::EL_LIBRARY] == 0 } {
					cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
					instant_interact_sig
				} else {
					cputs "\nSkipping expect-lite directive: $g_dir_str " $_el_info_color
				}
			}
			"\\*FORK*"	{	# Spawn/Switch to a new session
				set fork_list [split $g_dir_str]
				
				if { [llength $fork_list] > 2 } {
					if {$WARN} {
						cputs "\n\nWarning: FORK: Too Many Names: $g_dir_str\n" $_el_warn_color
					}
					# invalid *FORK command, exit this function
					return 1
				} elseif {[llength $fork_list] == 2 } {
					# set the fork_id to the passed value
					set fork_id [lindex $fork_list 1]
					# store current eols in current session fork_context
					set _el_fork($_el_current_session,eols) $_el_eols

				} elseif {[llength $fork_list] == 1 } {
					# no arguments to *FORK, then display current session name
					set fork_id $_el_current_session
				}
				# spawn/switch to fork_id session
				set _el_current_session [session_id_manager $fork_id]
				# set eols from new fork_id_context
				set _el_eols $_el_fork($_el_current_session,eols)


			}
			"\\*NOFAIL"	{	# Never fail the test
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				incr NOFAIL
			}
		}
		# end switch glob

		# set values with a parameter (using regex)
		switch -regexp $g_dir_str {	
			"\\*INFINITE_LOOP [0-9]+"	{	# expect-lite infinite loop value
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				set varlist [split $g_dir_str ]
				set _el_infinite_loop [lindex $varlist 1]
			}
			"\\*TCL .+" { #native TCL command for library use
				if { [info exist ::EL_LIBRARY] } {
					set ::_el_tcl_cmd [string trim $g_dir_str "*TCL "]
					# preserve timeout
					set el_timeout $::timeout
					# disable timeout during function call
					set timeout 0
					if { $DEBUG } {
						puts "EL TCL CMD>$::_el_tcl_cmd"
					}
					# provide catch protection, and run tcl_cmd
					if {[ catch { uplevel #0 {eval $::_el_tcl_cmd }}  error ]} {
						puts "ERROR: Expect: \'\n$::_el_tcl_cmd\' \n$error\n"
					}
					# restore timeout
					set timeout $el_timeout
				}
			}
			"\\*TIMESTAMP" { #Issue Time Stamps with each > or >>
				cputs "\nexpect-lite directive: $g_dir_str " $_el_info_color
				set ts_list [split $g_dir_str]
				if { [llength $ts_list] > 2 } {
					if {$WARN} {
						cputs "\n\nWarning: TIMESTAMP: Too Many Parameters: $g_dir_str\n" $_el_warn_color
					}
					# invalid *TIMESTAMP command, exit this function
					return 1
				} elseif {[llength $ts_list] == 2 } {
					# set timestamp on & format string
					set TIMESTAMP 1
					set ts_format [lindex $ts_list 1]
					switch -glob $ts_format {
						"YMD"	-
						"ISO"    { set _el(ts_fmt) "\n\[ %Y-%m-%d %T \] " }
						"MDY"	 { set _el(ts_fmt) "\n\[ %D %T \] " }
						"DMY"	 { set _el(ts_fmt) "\n\[ %d-%m-%Y %T \] " }
						default { # set a ISO as default
									if {$WARN} { cputs "\n\nWarning: unknown format, using YMD" $_el_warn_color }
									set _el(ts_fmt) "\n\[ %Y-%m-%d %T \] " 
								}
					}

				} elseif {[llength $ts_list] == 1 } {
					# no arguments to *TIMESTAMP, default to ISO
					set TIMESTAMP 1
					# use YMD format as default
					set _el(ts_fmt) "\n\[ %Y-%m-%d %T \] "
				}

			}

		}
		# end switch regex
		return 1
	}

	#
	#	Heart of expect-lite, reads through script file, and takes appropriate action (based on switch statement)
	#	
	#
	proc _el_script_exec {{cmd_file ""} {buf_stack ""}} {
		# default parameters: cmd_file is "" in library mode
		#	buf_stack is used when el_script is called recursively (e.g. include)
		#global remote_host _out _out_nowait _in _in_noregex _not_in _timeout _var _cond _cond_regex _cond_else _label  _el expect_out timeout _exec _stdout_comment _stdout_comment_nobuf _include _include_fail comment_line timeout_line timeout_multiplier _el_lines WARN delay_wait_for_host
		#global cli_namespace user_namespace timeout remote_host user_dir _el_infinite_loop _incr_var _decr_var _math_var connect_method send_slow _el_fork spawn_id DEBUG INFO arg0 _el_comment_color _el_info_color _el_warn_color _el_err_color _el_eols _el_fork_context _el_buf EXP_INFO
		#global _el_current_session NOFAIL TIMESTAMP
		global expect_out timeout spawn_id

		upvar expectlite::remote_host				remote_host
		upvar expectlite::_out						_out
		upvar expectlite::_out_nowait				_out_nowait
		upvar expectlite::_in						_in
		upvar expectlite::_in_noregex				_in_noregex
		upvar expectlite::_not_in					_not_in
		upvar expectlite::_timeout					_timeout
		upvar expectlite::_var						_var
		upvar expectlite::_cond 					_cond
		upvar expectlite::_cond_regex				_cond_regex
		upvar expectlite::_cond_else				_cond_else
		upvar expectlite::_label					_label
		upvar expectlite::_el						_el
		upvar expectlite::_exec 					_exec
		upvar expectlite::_stdout_comment			_stdout_comment
		upvar expectlite::_stdout_comment_nobuf 	_stdout_comment_nobuf
		upvar expectlite::_include					_include
		upvar expectlite::_include_fail 			_include_fail
		upvar expectlite::comment_line				comment_line
		upvar expectlite::timeout_line				timeout_line
		upvar expectlite::timeout_multiplier		timeout_multiplier
#		upvar expectlite::_el_lines 				_el_lines
		upvar expectlite::WARN						WARN
		upvar expectlite::delay_wait_for_host		delay_wait_for_host
		upvar expectlite::cli_namespace 			cli_namespace
		upvar expectlite::user_namespace			user_namespace
		upvar expectlite::remote_host				remote_host
		upvar expectlite::user_dir					user_dir
		upvar expectlite::_el_infinite_loop 		_el_infinite_loop
		upvar expectlite::_incr_var 				_incr_var
		upvar expectlite::_decr_var 				_decr_var
#		upvar expectlite::_math_var 				_math_var
		upvar expectlite::connect_method			connect_method
#		upvar expectlite::send_slow					send_slow
		upvar expectlite::_el_fork					_el_fork
		upvar expectlite::DEBUG 					DEBUG
		upvar expectlite::INFO						INFO
		upvar expectlite::arg0						arg0
		upvar expectlite::_el_comment_color 		_el_comment_color
		upvar expectlite::_el_info_color			_el_info_color
		upvar expectlite::_el_warn_color			_el_warn_color
		upvar expectlite::_el_err_color 			_el_err_color
		upvar expectlite::_el_eols					_el_eols
#		upvar expectlite::_el_fork_context			_el_fork_context
		upvar expectlite::_el_buf					_el_buf
		upvar expectlite::EXP_INFO					EXP_INFO
		upvar expectlite::_el_current_session		_el_current_session
		upvar expectlite::NOFAIL					NOFAIL
		upvar expectlite::TIMESTAMP 				TIMESTAMP



		set prev_line ""
		set comment_line ""
		set timeout_line ""
		set _el_fail_script ""
		set teststr ""

		# clear expect lines list
		set _el_cont_lines ""

		# set default session (aka fork)
		set _el_current_session $_el(fork_current)
		#fork_id

		#
		# Dereference Vars if needed in $_el(line) as read from cmd file	
		#	
		proc _el_deref_line {} {
			#global _el _var _cond
			upvar expectlite::_el					_el
			upvar expectlite::_var					_var
			upvar expectlite::_cond					_cond
			
			if { [regexp {.*\$[A-Za-z0-9]+.*} $_el(line) ] } {
				# Detect the use of a user_var
				#puts "l0>$_el(line)"
				switch -regexp -- $_el(line) {
					{^\$.*} -				
					{^\+\$.*} {
						# this is a dyn var definition line - remove leading '+'
						if { [regexp -line {^\+\$.*} $_el(line)] == 1} {
							set prepend "+"
							# remove leading '+'
							regsub {^\+} $_el(line) {} _el(line)

							} else {set prepend ""}
						# this is a var definition line - don't dereference first var (it is being set)
						set parse_list1 [split $_el(line) "="]
						# check that this is an assignment, and not an increment var command
						if { [llength $parse_list1] == 2 } {
							# check if there are other variables on left side of equals assignment
							set _left_vars [lindex $parse_list1 0]
							set _el_var_list [split $_left_vars "$"]
							#puts "========>$_el_var_list"
							# is there more than one var on the left-side of the assignment?
							if { [llength $_el_var_list] > 2 } {
								set _left_vars [join [lrange $_el_var_list 1 end ] "$"]
								# dereference left-side vars except first one
								set _left_vars [parse_var $_left_vars $_var]
								set _left_vars "\$[lindex $_el_var_list 0]$_left_vars"
								# detect illegal chars in var name?
								#regsub {[^A-Za-z0-9_$]} $_left_vars {} _left_vars
							}
							# 1st pass of dereferncing assignment variables
							set _el_assignment [parse_var [lindex $parse_list1 1] $_var]
							# 2nd pass of dereferencing assignment variables
							set _el(line) "$_left_vars=[parse_var $_el_assignment $_var]"
						}
						# place '+' back on front when needed
						set _el(line) "$prepend$_el(line)"
						#puts "l2>$_el_assignment|"
						#puts "l2>$_el(line)"
					} 
					{^\-\$.*} {
						# this is a decr var line - don't touch it
					}	
					{^\=\$.*} {
						# this is a math var definition line - don't dereference first var (it is being set)
						# separate var from rest of line with space
						regsub {^(=\$[A-Za-z0-9_]+)}  $_el(line) {\1 } _el(line)
						set parse_list1 [split $_el(line) " "]
						set _math_var [lindex $parse_list1 0]
						# remove math variable from list
						set parse_list1 [lreplace $parse_list1 0 0]
						set _parse_line [ join $parse_list1 " " ]
						#puts "l2.1>$_parse_line"
						# first pass at dereferencing variables
						set _parse_line [parse_var $_parse_line $_var]
						set _el(line) "$_math_var  [parse_var $_parse_line $_var]"

						#puts "l2>$_el(line)"
					} 
					{^\#.*} { 
						# this is a comment line - don't touch it
					} 
					{^\?.+\?.+} {
						# This is a conditional line, only dereference Vars in Conditional part
						# remove "if" if present
						regsub {^\?[iI]?[fF]?} $_el(line) "?" _el(line)
						# insert spaces around conditional operator to permit proper resolving of variables
						# Only act on conditional part of the line (between the ?'s)
						#regsub {([<>][^^][=]?)} $_el(line) { \1 } _el(line)
						regsub {\?(.+)([<>][=]?)(.+)\?(.+)} $_el(line) {? \1 \2 \3 ?\4} _el(line)
						#puts "PL0>$_el(line)"
						# parse cond from action
						#set parse_list1 [split $_el(line) "?"]

						set parse_list1 ""
						# remove first '?' on conditional line
						regsub {^[ \t]*[?]} $_el(line) {} _el(line)
						# parse cond from action
						lappend parse_list1 [string range $_el(line) 0 [expr [string first $_cond $_el(line)] - 1]]
						lappend parse_list1  [string range $_el(line) [expr [string first $_cond $_el(line)] + [string length $_cond] ] [string length $_el(line)]]

						#puts "PL1>$parse_list1|[llength $parse_list1]"
						if { [llength $parse_list1] >= 2 } {
							# rebuild _el_line: ?cond?$action_str
							# build action assigment and else_action without dereferencing
							set action_str " [join [lrange $parse_list1 1 [llength $parse_list1] ] ""]"
							# first pass dereferencing
							set _parse_line [parse_var [lindex $parse_list1 0] $_var ]
							set _el(line) "?[parse_var $_parse_line $_var]? $action_str"
						}
						#puts "EL_LINE_COND>$_el(line)"

					}
					default {
						#puts "EL_LINE1>$_el(line)"
						# Process Line resolving vars if needed
						set parse_list1 [split $_el(line) ]
						#puts "p1>$parse_list1"
						set plist2 {}
						foreach i $parse_list1 {
							lappend plist2 [ parse_var $i $_var ]
						}
						set _el(line2) [join $plist2 " "]
						set _el(line) $_el(line2)
						# are there still vars to be dereferenced, if so, to a second pass
						if { [regexp {.*\$[A-Za-z0-9]+.*} $_el(line) ] } {
							# Process Line resolving vars if needed
							set parse_list1 [split $_el(line) ]
							#puts "p1>$parse_list1"
							set plist2 {}
							foreach i $parse_list1 {
								lappend plist2 [ parse_var $i $_var ]
							}
							set _el(line2) [join $plist2 " "]
							set _el(line) $_el(line2)
						}
						#puts "EL_LINE2>$_el(line)"

					} 
				} 
				#switch
			} 
			#if
		} 
		#proc _el_deref_line


		#
		#	Execute expect lines buffered in list variable _el_lines 
		#
		#
		proc _el_exec_expect {} {
			#Allow embedded Expect access to only the following internal vars
			#global cli_namespace user_namespace timeout remote_host user_dir 
			global errorInfo 
			upvar  expectlite::DEBUG 					DEBUG

			upvar _el(_el_lines) __el_lines _el_cont_lines __el_cont_lines 

			#puts "=>[llength _el_lines]:$_el_lines:"
			if {  $__el_lines != {} } {

				# join Expect lines		
				set __el_cont_lines [join $__el_lines " ; "]
				if {$DEBUG} {puts "EL-Expect: $__el_cont_lines \n"}

				if {[ catch { uplevel {eval $_el_cont_lines }}  error ]} {
					regsub -all " ; " $__el_cont_lines "\n" __el_cont_lines
					puts "ERROR: Expect: \'\n$__el_cont_lines\' \n$errorInfo\n"
				}

				# clear cont lines
				set __el_lines {}
				# re-derefence variables in $_el(line)
				#	required when embedded expect changes user_namespace(variable) values
				_el_deref_line
			}	
		} 
		# end of _el_exec_expect


		# idea: if $cmd_file begins with '|' then process as a _el(line) rather than a file
		# 	Used for conditional actions, which are a recursive call to _el_script_exec
		#	Also used by interact to execute lines of expect-lite script
		# initialize vars
		set _one_line 0

		if { $cmd_file == "" } {
			# use pre-read _el_buf(stack)
			# read first line of buffer
			set _el(line) [_el_buffer_read  $_el_buf($buf_stack,ptr) $buf_stack]

		} elseif { [ regexp {^\|} $cmd_file ] == 0} {
			#puts "zzz>$cmd_file|[ regexp {^\|} $cmd_file ]"
			# read in cmd_file to buffer
			set buf_stack [_el_buffer $cmd_file]
			# change to passed directory, if needed (and set term type & shell) for main script
			if {$cli_namespace($arg0) == [parse_cmd_file_nopath  $cmd_file]} {
				# is main script name equal to command file (script) name 
				# or Is this an include file, then don't do remote_host_init
				read_set_config_directives $buf_stack
				# init prompt for first (default) session
				remote_host_init $user_dir
				# kick the prompt
				protected_send "$_el_eols"
			}

			# set default fork session if it doesn't already exist
			if { [info exists _el_fork(default,session)] != 1 } {
				if { [catch { set _el_fork(default,session) $spawn_id  } error] } {
					puts stderr "BUG: Expect-lite: Bug in _el_script_exec init code while setting default FORK session \n   $error"
					# Fail script?
					exit 1
				}
			}

			# get first line of cmd_file from buffer
			set _el(line) [_el_buffer_read  $_el_buf($buf_stack,ptr) $buf_stack]
			# remove leading white space on line (permitting indentation
			regsub {^[ \t]+} $_el(line) {} _el(line)
			set _one_line 0

		} else {
			# cmd_file is string to evaluate (trim leading spaces if needed)
			regsub {\|[ ]*(.*)} $cmd_file {\1} _el(line)
			set _one_line 1
		}
		# set test up for success
		set _el(success) 1
		while { -1 != $_el(line) } {
			# deref line
			_el_deref_line


			#puts "el(line):$_el(line)"


			switch -regexp -- $_el(line) {
				^\#	{	# Don't send lines that start with a '#' which are commented

						print_comment_line

					}
				{(^>|^>>)\^[A-\]a-z]}	{
						# Don't wait for prompt, Generalized routine to send control chars

						# get ascii value of char
						scan [string index $_el(line) [expr [string length $_el(line)] -1]] %c control_char_ascii
						# parse char from passed string
						set control_char [string index $_el(line) [expr [string length $_el(line)] -1]]
						# print timestamp
						if { $TIMESTAMP } {
							puts -nonewline [timestamp -format $_el(ts_fmt)]
						}
						# Check if passed char is lower case
						if {[expr $control_char_ascii - 96] > 0 } {
							# Send lower case control char
							send [format %c [expr $control_char_ascii - 96]]
						} else {
							# Send upper case control char
							send [format %c [expr $control_char_ascii - 64]]
						}
						#puts "=>$control_char_ascii"
						# notify user
						puts "sending ^$control_char\n"
						set prev_line $_el(line)
					}
				^\>>	{	# Don't wait for Prompt just send the line

						print_comment_line
						# execute expect lines if present
						_el_exec_expect

						# delay to give remote-host a little time (in ms)
						after 10
						# print timestamp
						if { $TIMESTAMP } {
							puts -nonewline [timestamp -format $_el(ts_fmt)]
						}
						# Check of remote_host is still connected before sending string
						if { [catch { send  "[_el_strip_char $_el(line) $_out_nowait]$_el_eols"  } error] } {
							cputs "CONNECT ERROR: Lost connection to $remote_host \n   $error \nExiting..." $_el_err_color
							# Fail script
							exit 1
						}
						set prev_line $_el(line)
						# clear expect buffer
						expect -re {.*} {}

					}
				^\>	{	# Wait for Prompt and send the line

						# do not wait for prompt if only one_line
						if { $_one_line == 0 } {
							wait_for_prompt
						}
						print_comment_line
						# execute expect lines if present
						_el_exec_expect

						# Set test up for success
						set _el(success) 1
						# print timestamp
						if { $TIMESTAMP } {
							puts -nonewline [timestamp -format $_el(ts_fmt)]
						}

						# Check of remote_host is still connected before sending string
						if { [catch { send   "[_el_strip_char $_el(line) $_out]$_el_eols"  } error] } {
							puts stderr "CONNECT ERROR: Lost connection to $remote_host \n   $error \nExiting..."
							# Fail script
							exit 1
						}
						set prev_line $_el(line)
						# clear expect_out buffer
						expect -re {.*} {}
						#set expect_out(buffer) ""
						if {$connect_method == "none"} {
							# delay in ms - slow down the host, give the target time to respond
							after [expr $delay_wait_for_host * 2]
						}	
					}
				^\<\<	{ 	# Expect teststr without regex or set success to 0 and fail test		'<<'=no regex
						set _el(success) 0
						print_comment_line
						# execute expect lines if present
						_el_exec_expect
						set teststr [_el_strip_char $_el(line) $_in_noregex]

						# catch expect, just in case
						if {[catch {expect -ex "$teststr" { set _el(success) 1 } } error  ] } {
							if {$WARN} {
								cputs "\nExpect error: Expect-lite: $error\n" $_el_err_color
							}
						}

						# look at the expect_out(buffer) for debugging
						if { $DEBUG } {
							puts "\nfind<<$teststr>>"
							puts "  in<<$expect_out(buffer)>>"
							#puts "  found_in<<$expect_out(0,string)>>"
							puts "\n"
						}
						# show expect found string
						if { $EXP_INFO && $DEBUG==0 && $_el(success)==1 } {
							cputs "<<$expect_out(0,string)" $_el_info_color
						}
					}
				^\<	{ 	# Expect teststr or set success to 0 and fail test	'<'=use regex
						set _el(success) 0
						print_comment_line
						# execute expect lines if present
						_el_exec_expect
						set teststr [_el_strip_char $_el(line) $_in]

						# test for single num expect string
						if {([string length $teststr]==1) && ([regexp \[0-9\] $teststr])} {
					    	expect {
								 -re \n$teststr\r { 
					 		    	#puts "\nGood=[_el_strip_char $_el(line) $_in]\n" 
							    	set _el(success) 1
							    	} 
						    	}
						} else {
							# test for multi-char expect strings
							# catch expect, just in case
							if {[catch {expect -re "$teststr" { set _el(success) 1 } } error  ] } {
								if {$WARN} {
									cputs "\nWarning: Expect-lite: $error: Attempting to fix\n" $_el_warn_color
								}
								# escape parens or square brackets
								regsub {([^\\])[(]} $teststr {\1\(} teststr
								regsub {([^\\])[)]} $teststr {\1\)} teststr
								# fix potential unbalanced square brackets
								regsub {([^\\])[[]} $teststr {\1\[} teststr
								regsub {([^\\])[]]} $teststr {\1\]} teststr
								# escape braces
								regsub {([^\\])[\{]} $teststr {\1\{} teststr
								regsub {([^\\])[\}]} $teststr {\1\}} teststr
								#puts "+>$teststr"
								if {[catch {expect -re "$teststr" { set _el(success) 1 } } error  ] } {
									puts "\nBUG: Expect-lite: $error\n Fix Failed\n"
								}
							}
					 		#puts "\nGood=[_el_strip_char $_el(line) $_in]\n" 
						}
						# look at the expect_out(buffer) for debugging
						if { $DEBUG } {
							puts "\nfind<<$teststr>>"
							puts "  in<<$expect_out(buffer)>>"
							#puts "  found_in<<$expect_out(0,string)>>"
							puts "\n"
						}
						# show expect found string
						if { $EXP_INFO && $DEBUG==0 && $_el(success)==1} {
							regsub ".*($teststr)" $expect_out(buffer) {\1} foundstr
							cputs "<$foundstr" $_el_info_color
						}

					}
				^\-<	{ 	# NOT Expect teststr and If it is found then set success to 0 and fail test
						set _el(success) 0
						print_comment_line
						# execute expect lines if present
						_el_exec_expect
						set teststr [_el_strip_char $_el(line) $_not_in]

						# delay in ms - slow down the host, give the target time to respond
						after $delay_wait_for_host	

						# wait for something in expect_out buffer if using localhost
						if {$connect_method == "none"} {
							expect {
								-timeout 1 -notransfer -re {\n.+\n} {}
								}
						}

						# search all text in expect in buffer -- using -notransfer to keep data in expect input buffer
						# Reverse logic, if the NOT match string is found, then set _el(success) to 0 and fail test
						expect {
							-timeout 0 -notransfer -re  $teststr {set _el(success) 0} 
							default {set _el(success) 1}
							}
						# look at the expect_out(buffer) for debugging
						if { $DEBUG } {
							puts "\nNOT find<<$teststr>>"
							puts "  in>>$expect_out(buffer)<<"
							puts "\n"
						}

					}
				^\@ 	{ 	# Update the timeout value
						
						# validate that the timeout value is a number
						print_comment_line
						# execute expect lines if present
						_el_exec_expect
						if {([regexp \[^0-9\ \]+ [_el_strip_char $_el(line) $_timeout]])} {
							cputs "ERROR: Bad Timeout Value: [_el_strip_char $_el(line) $_timeout] " $_el_err_color
							send "quit$_el_eols"
							# Stop the script Now!
							exit 1
						} else {
							#puts "\nSetting Expect Timeout to: [_el_strip_char $_el(line) $_timeout] \n"
							set timeout_int [_el_strip_char $_el(line) $_timeout]

							if { $cli_namespace($timeout_multiplier) != 1 } {
								# user has changed timeout_multiplier to something other than 1, use the value
								set timeout [expr $timeout_int * $cli_namespace($timeout_multiplier)]
								set timeout_line "\nTimeout Multiplier mode: Setting Expect Timeout to: $timeout \n"
							} else {
								set t_timeout $timeout_int
								set timeout_line "\nSetting Expect Timeout to: $t_timeout \n"
								set timeout $t_timeout
							}
						}
						# print new timeout value if one_line is set (e.g. from IDE)
						if { $_one_line == 1 } {
							cputs $timeout_line $_el_info_color
						}
					}
				^\\\?[iI]?[fF]?	{	# Conditional processing - ?cond?action::else_action

						# execute expect lines if present
						_el_exec_expect

						print_comment_line

						set _cond_line [_el_strip_char $_el(line) $_cond_regex]
						#puts "\n##>[_el_strip_char $_el(line) $_cond_regex]\n"
						# remove optional 'if' or 'IF'
						regsub {^[iI]?[fF]?} $_cond_line "" _cond_line

						#parse conditional and action (and else_action if needed)
						set _else_action ""
						set _action [string range $_cond_line [expr [string first $_cond $_cond_line] + [string length $_cond] ] [string length $_cond_line]]
						#puts "\n##ACT>$_cond_line|$_action|[string first $_cond $_cond_line]|[string length $_cond]|[string length $_cond_line]"
						if { [regexp $_cond_else $_action] } {
							# add spaces to else separator
							regsub "$_cond_else" $_action " $_cond_else " _action
							# parse action into action::else_action
							set _else_action [string range $_action [expr [string first $_cond_else $_action] + [string length $_cond_else] ] [string length $_action]]
							set _action [string range $_action 0 [expr [string first $_cond_else $_action] - [string length $_cond_else] ]]
							# trim trailing spaces
							set _action [string trimright $_action]
							#puts "#2#>then$_action|else$_else_action|"
						}
						set _conditional [string range $_cond_line 0 [expr [string first $_cond $_cond_line] - [string length $_cond] ]]
						# check for unresolved Var
# 						# replace all unresolved vars with ""
# 						regsub -all {\$[a-zA-Z0-9_]+} $_conditional {""} _conditional

						# define list of supported conditional operators
						set _cond_oper_list { == != <= >= < > }

						# find conditional operator
						set _cond_oper " "
						foreach k $_cond_oper_list {
							if { [regexp $k $_conditional] } {
								set _cond_oper $k
								break
							}
						}
						 # parse around conditional operator
						set _cond_arg1 [string range $_conditional 0 [expr [string first $_cond_oper $_conditional] - 1]]
						set _cond_arg2 [string range $_conditional [expr [string first $_cond_oper $_conditional] + [string length $_cond_oper] ] [string length $_conditional]]
						#puts "\n#@@|$_cond_oper|$_conditional|$_cond_arg1|$_cond_arg2|"

						# remove leading and trailing spaces from conditional arguments
						# count the number of non-space chars in condition
						if {[string length [regexp -inline {[^ ]+} $_cond_arg1]] > 1 } {
							# condition larger then 1 byte
							regsub -all {([ ]*)([^ ].*[^ ])([ ]*)} $_cond_arg1 {\2} _cond_arg1
						} else {
							# condition only 1 byte in size
							regsub -all {([ ]*)([^ ]*)([ ]*)} $_cond_arg1 {\2} _cond_arg1
						}
						
						if { [string length [regexp -inline {[^ ]+} $_cond_arg2]] > 1 } {
							# condition larger then 1 byte
							regsub -all {([ ]*)([^ ].*[^ ])([ ]*)} $_cond_arg2 {\2} _cond_arg2
							#puts ">$_cond_arg2<"
						} else {
							# condition only 1 byte in size
							regsub -all {([ ]*)([^ ]*)([ ]*)} $_cond_arg2 {\2} _cond_arg2
							#puts ">$_cond_arg2<"
						}

						# preserve buffer pointer - if not one_line
						if { $_one_line != 1 } {
							set _tmp_buf_ptr $_el_buf($buf_stack,ptr)
						}

						# validate comparison and if true, send to _el_script_exec (recursive call)

						# check for unresolved Var
						# Var is defined as in the set [a-zA-Z0-9_]
						if { [regexp {\$[a-zA-Z0-9_]+} $_conditional] } {
							if {$DEBUG} { puts "EL:Unresolved Var:$_conditional"}
							# replace all unresolved vars with ""
							regsub -all {\$[a-zA-Z0-9_]+} $_cond_arg1 {} _cond_arg1
							regsub -all {\$[a-zA-Z0-9_]+} $_cond_arg2 {} _cond_arg2							
						} 
						# build test line
						set _test " expr { \"$_cond_arg1\" $_cond_oper \"$_cond_arg2\" } "
						# check for syntax errors and evaluate result of test line
						if {[catch { eval  $_test  } error ] } {
							if {$WARN} {
								cputs "\nWarning: Expect-lite: $error: Unable to detect conditional operator\n" $_el_warn_color
								cputs "	Evaluating line:$_test|$_action|$_else_action" $_el_warn_color
							}
						} else {
							# Show user the if statement, action and result

							if {$INFO} {
								set _test_result [eval  $_test ]
								if {$_test_result} {
									set _test_result "TRUE"
								} else {
									set _test_result "FALSE"
								}
								if { $_else_action == ""} {
									cputs "\nIf:$_test|then$_action|result=$_test_result\n" $_el_info_color
								} else {
									cputs "\nIf:$_test|then$_action|else$_else_action|result=$_test_result\n" $_el_info_color
								}
							}
							# no syntax error, go ahead and eval the conditional action or else action
							# skip to label if present in action or else_action
							if { [eval  $_test ] } {
								# remove any leading space and trailing from action
								regsub {^[ \t]+} $_action {} _action
								regsub {[ \t]+$} $_action {} _action
								# Is there a jump to a label in action?
								if { [ regexp "^$_label" $_action ] } {
									set _el_buf($buf_stack,ptr) [_el_buffer_search $_el_buf($buf_stack,ptr) $_action $buf_stack]
								} else {
									# Interpret action with recursive call to _el_script_exec
									_el_script_exec "\|$_action" $buf_stack
								}
							} elseif { $_else_action != "" } {
								# remove any leading and trailing space from else_action
								regsub {^[ \t]+} $_else_action {} _else_action
								regsub {[ \t]+$} $_else_action {} _else_action
								# Is there a jump to a label in else_action?
								if { [ regexp "^$_label" $_else_action ] } {
									set _el_buf($buf_stack,ptr)  [_el_buffer_search $_el_buf($buf_stack,ptr) $_else_action $buf_stack]
								} else {
									# Interpret else_action with recursive call to _el_script_exec
									_el_script_exec "\|$_else_action" 
								}
							}
						}
						if { $_one_line != 1 } {					
							# label not found
							if { $_el_buf($buf_stack,ptr) == -1 } {
								# label search failed (-1), restore buf_ptr, allow script to continue
								set _el_buf($buf_stack,ptr) $_tmp_buf_ptr
							}

							# infinite loop detected!
							if { $_el_buf($buf_stack,ptr) == -2 } {
								# label search failed (-2), restore buf_ptr, allow script to continue
								set _el_buf($buf_stack,ptr) $_tmp_buf_ptr
								# signal to fail script - something is wrong
								set _el(success) 0
								# tell the user why this failed
								set prev_line "Infinite Loop Detected!"
							}
							#puts "++++++ptr>$_el_buf($buf_stack,ptr)"
						}
					}
				^\;;	{	# Send "comment" to stdout unbuffered

						# execute expect lines if present
						_el_exec_expect

						# Find color at beginning of comment line and use it, if not, use default comment color
						set unbuf_comment [_el_strip_char $_el(line) $_stdout_comment_nobuf]
						set unbuf_color [find_color $unbuf_comment] 
						if { $unbuf_color != "" } {
							# remove first word
							regsub "$unbuf_color" $unbuf_comment "" unbuf_comment
							# use custom color
							cputs $unbuf_comment $unbuf_color
						} else {
							# use default comment color
							cputs $unbuf_comment $_el_comment_color
						}

					}
				^\;	{	# Send "comment" to stdout buffered

						# execute expect lines if present
						_el_exec_expect

						print_comment_line

						set comment_line [_el_strip_char $_el(line) $_stdout_comment]
						# print comment if one_line is set
						if {$_one_line} { print_comment_line }

					}
				^\~ 	{	# include file from same location at cmd_file
						# Uses recursion to interpret included file

						# execute expect lines if present
						_el_exec_expect

						print_comment_line

						set _include_file [_el_strip_char $_el(line) $_include]
						#puts "==========> $_include_file"
						# don't include file if name is set to __NO_STRING_CAPTURED__ (which is a blank dynamic/capture variable)
						if { $_include_file != "__NO_STRING_CAPTURED__" } {
							# absolute path to include file?
							set _slash "/"
							if { [string index $_include_file 0] == $_slash} {
								set include_path_file "$_include_file"
							} else {
								set include_path_file "[parse_cmd_file_path $cmd_file]$_include_file"
							}
							#puts "->$include_path_file"
							if {$INFO} {
								cputs "\nIncluding: $_include_file\n" $_el_info_color
							}
							# Make Recursive Call in stand alone mode
							if { [info exist ::EL_LIBRARY] != 1 } {
								set result [_el_script_exec $include_path_file]
								# don't report false include failure, set test_failed
								switch -- $result {
									0 { # pass
										}
									1 { # fail
										set _el(success) 1
										set _el(test_failed) 1
										}
									2 { # fail right now!
										# return result code to caller, stop now
										return $result
										}
								}; #switch
							} else {
								cputs "\nWarning: Skipping include file:$_include_file\n" $_el_warn_color
							}

							# pop _el_buf(stack)
							incr _el_buf(stack) -1

						}

					}
				^\\+\\$[A-Za-z0-9_]+[=]	{ 	# Setup Dynamic Var using Expect Capture
						# execute expect lines if present
						_el_exec_expect

						print_comment_line
						# delay in ms - slow down the host, give the target time to respond
						after $delay_wait_for_host
						# parse $_el(line) to pull out var and expect parts
						#puts "\n->$_el(line)"
						set user_var [string range $_el(line) 1 [expr [string first "=" $_el(line)] -1 ]]
						set user_value [string range $_el(line) [expr [string first "=" $_el(line)] +1 ] [string length $_el(line)]]
						#puts "\n->$user_var=$user_value"
						# remove '$' in front of the Var
						regsub {\$} $user_var "" user_var

						#set teststr [_el_strip_char $_el(line) $_setvar]
						set capture_val "__NO_STRING_CAPTURED__"
						set _el(var_capture_begin) [ clock seconds ]
						expect {
							-notransfer -re "\n$_el(prompt)|\n.*\\\$ $|\n.*# $|\n.*> $|\n.*% $" {

								# Do capture in expect buffer
								if { [catch {regexp -line "$user_value" $expect_out(buffer) match_val capture_val}]  } {
									set capture_val "__NO_STRING_CAPTURED__"
									set match_val ""
								}
								# remove newlines from capture value
								regsub -all {[\n\r]+} $capture_val "" capture_val
								if { $DEBUG } {
									puts "\nvar_found<<$capture_val>>"
									puts "\nvar_in<<$expect_out(buffer)>>"
								}
								if { $INFO } {
									#puts "\nAssigned Var:$user_var=|$capture_val|\n"
									cputs "\nAssigned Var:$user_var=$capture_val\n" $_el_info_color
								}
								# Trigger a new prompt - commented out cvm 10/2/10
								#protected_send "$_el_eols"
								}
							# expect should never timeout, but just in case
							timeout {
								set _el(var_capture_end)	[ clock seconds ]
								set _el(var_capture_duration) [ expr $_el(var_capture_end) - $_el(var_capture_begin)]
								if { $timeout != $_el(var_capture_duration)} {
									puts "BUG: Expect-lite: Var Capture Timed Out!\n"
									puts "Debugging Info: \n\tEL_Line:$_el(line)\n\texpect timeout=$timeout secs \n\tDuration of capture:$_el(var_capture_duration) secs \n\n"
									show_vars
								} elseif {$WARN} {
									cputs "Warning: Expect-lite: Var Capture Timed Out! Recommend lengthening timeout, current timeout is: $timeout secs\n" $_el_warn_color
									if { $DEBUG } {
										puts "\nfound<<$capture_val>>"
										puts "\nin<<$expect_out(buffer)>>"
									}

								}
							}
						}
						# set var (similar to below)
						set user_namespace($user_var) $capture_val
					}
				{^\$[A-Za-z0-9_]+[=]([ ]?|[~\$\[/A-Za-z0-9_.-]+)} {
						# allow a single space or blank as a value in addition to the normal assignment

						# execute expect lines if present
						_el_exec_expect

						# Set a user variable 

						set user_var [string range $_el(line) 1 [expr [string first "=" $_el(line)] -1 ]]
						set user_value [string range $_el(line) [expr [string first "=" $_el(line)] +1 ] [string length $_el(line)]]

						# remove '$' in front of the Var
						regsub {\$} $user_var "" user_var

						# Assign the Var
						set user_namespace($user_var) $user_value

						# Used for debugging
						#puts "->$user_var $user_value"
						#parray user_namespace
						}
				^\\+\\$[A-Za-z0-9_]+	{ 	# increment Var, decimal 
						# execute expect lines if present
						_el_exec_expect

						print_comment_line

						set _tmp_var [_el_strip_char $_el(line) $_incr_var]
						#increment decimal number if possible
						catch { incr user_namespace($_tmp_var)}

						# else, pass string value back without incrementing					
					}
				^\-\\$[A-Za-z0-9_]+	{ 	# decrement Var 
						# execute expect lines if present
						_el_exec_expect
						print_comment_line

						set _tmp_var [_el_strip_char $_el(line) $_decr_var]
						# decrement variable
						catch { incr user_namespace($_tmp_var) -1 }
						#puts "->$_el(line) $_decr_var $_tmp_var"

					}
				{^=\$[A-Za-z0-9_]+}	{ 	# math variable
						# execute expect lines if present
						_el_exec_expect
						print_comment_line

						# supports oper: << >> & | ^ + = * / % pow(x,y)

						# strip math_var symbol from beginning of line
						regsub {^=\$} $_el(line) "" _tmp_var
						set _el_parse_list [split $_tmp_var " "]
						set var [lindex $_el_parse_list 0]
						# remove $var from list
						set _el_parse_list [lreplace $_el_parse_list 0 0]
						set _el_predicate [join $_el_parse_list " "]
						# check if math variable exists
						if { [info exists user_namespace($var)] == 0 } {
							# if not, set to blank
							set user_namespace($var) ""
						}
						if { [catch { set user_namespace($var) [expr $user_namespace($var)$_el_predicate] } error]} {
							if {$WARN} {
								cputs "\nWarning: Expect-lite: Unable to interpret $_el(line) \n	$error" $_el_warn_color
							}
						}

					}
				^\!	{	# Run an Embedded Expect Line(s) (limited support)

						# don't add Expect comment lines (hint they start with '!#'
						if { [regexp -line {^![ \t]*#} $_el(line) ] == 0  } {
							# add Expect Lines to _el(_el_lines) list
							lappend _el(_el_lines) [_el_strip_char $_el(line) $_exec] 
							#puts "+>[llength $_el(_el_lines)]:$_el(_el_lines):"

						}
					}
				^\\*~ 	{	# include file of failure script - called when test fails
							# Uses recursion to interpret included file
							# expect-lite directive

						# execute expect lines if present
						_el_exec_expect
						
						print_comment_line

						if { $_one_line == 0 } {
							set _el_fail_script [_el_strip_char $_el(line) $_include_fail]
						} else {
							# we are inside a recursive call
							uplevel {set _el_fail_script [_el_strip_char $_el(line) $_include_fail]}
						}
						# don't include file if name is set to __NO_STRING_CAPTURED__ (which is a blank dynamic/capture variable)
						if { $_el_fail_script == "__NO_STRING_CAPTURED__" } {
							# don't set fail_script
							set _el_fail_script ""
						}
						if { $_one_line == 0 } {
							if {$INFO} {
								cputs "\nFail Include Script: $_el_fail_script\n" $_el_info_color
							}
						} else {
							# we are inside a recursive call
							if {$INFO} {
								uplevel {cputs "\nFail Include Script: $_el_fail_script\n" $_el_info_color}
							}
						}

					}
				^\\*/.*/$	{	# Set a user defined prompt
							# expect-lite directive

						# execute expect lines if present
						_el_exec_expect

						# strip leading and trailing slashes
						regsub {^\*/} $_el(line) {} el_u_prompt
						regsub {/$} $el_u_prompt {} _el(prompt)
						#  Set empty prompt to something sane, the default prompt
						if { $_el(prompt) == "" } {
							set _el(prompt) $_el(default_prompt)
						}

						if $INFO {
							if { $_el(prompt) == $_el(default_prompt) } {
								cputs "INFO: User Defined Prompt is now default: $_el(prompt)" $_el_info_color
							} else {
								cputs "INFO: User Defined Prompt: $_el(prompt)" $_el_info_color
							}
						}

					}
				^\\*[a-zA-Z]	{	# Process expect-lite run-time directives
								# expect-lite directives

						print_comment_line

						# execute expect lines if present
						_el_exec_expect
						print_comment_line
						# Process the directive - return 0 to fail
						set _el(success) [_el_global_directive $_el(line)]

					}
				default {	# blank lines are allowed

			 		}
			}

			#puts "line_ptr->$_el_buf($buf_stack,ptr)|$_el(success)"

			# Fail the test if Expect fails
			if {$_el(success) != 1 && [regexp "^$_in" $_el(line)]==1  } {
				cputs "\n\n FAILED COMMAND:[_el_strip_char $prev_line $_out] \n" $_el_err_color
				cputs "    Expect Failed:$teststr\n" $_el_err_color
				if { $_el_current_session != "default" } {
					cputs "    Current Fork Session: $_el_current_session \n\n" $_el_err_color
				}
			}
			# Fail the test if NOT Expect fails
			if {$_el(success) != 1 && [regexp "^$_not_in" $_el(line)]==1  } {
				cputs "\n\n FAILED COMMAND:[_el_strip_char $prev_line $_out] \n" $_el_err_color
				cputs "    Expect Found:$teststr\n\n" $_el_err_color
				if { $_el_current_session != "default" } {
					cputs "    Current Fork Session: $_el_current_session \n\n" $_el_err_color
				}
			}
			# Fail the test, and invoke Fail_script
			if {$_el(success) != 1 } {
				if { $_el_fail_script != "" } {
					# run fail script to clean up
					# absolute path to fail include file?
					set _slash "/"
					if { [string index $_el_fail_script 0] == $_slash} {
						set include_path_file "$_el_fail_script"
					} else {
						set include_path_file "[parse_cmd_file_path $cmd_file]$_el_fail_script"
					}
					#puts "->$include_path_file"
					if {$INFO} {
						cputs "\nIncluding Fail Script: $_el_fail_script\n" $_el_info_color
					}
					# set fail_script up for success
					set _el(success) 1

					# Make Recursive Call
					_el_script_exec $include_path_file
					# special short cut to allow main script to continue
					if { [info exists _el(continue)] == 1 } {
						if { $_el(continue) == 1 } { set _el(success) 1 }
						set _el(continue) 0
					} else {
						# no backdoor to continue script, fail script
						set _el(success) 0
					}
					#puts "success->$_el(success)"
					#puts "FS>$_el_fail_script"	
				}
				# last chance to let script continue, even though it should fail
				if {$NOFAIL} {
					set _el(success) 1
					set _el(test_failed) 1
				} 			
				if { $_el(success) != 1 } {
					# allow fail script to set success, and continue if need be
					#protected_send "quit$_el_eols"
					#exit 1
					# FATAL
					return 2
				}
			}

			# ensure we stop at end of file/buffer
			if { $_one_line == 0 } {
				if { $_el_buf($buf_stack,ptr) >= [llength $_el_buf($buf_stack)] } {
					# exit the while loop
					set _el(line) -1
				}
				if {$_el_buf($buf_stack,ptr) < 0 } {
					# last chance to return unknown
					return -1
				}
			}
			# Get next line in buffer
			# Or return if in one_line mode
			if { $_one_line == 0 } {
				# not in one line, normal processing
				# read next line from buffer
				incr _el_buf($buf_stack,ptr)
				set _el(line) [_el_buffer_read $_el_buf($buf_stack,ptr) $buf_stack]
				# remove leading white space on line (permitting indentation)
				regsub {^[ \t]+} $_el(line) {} _el(line)
			} else {
				# one_line is true (which means invoked by a conditional)
				# Done processing one line, and handling failures
				# so just return from this recursive call
				return
			}

		}
		# set result code
		if { $_el(test_failed) == 1} {
			# FAIL
			return 1
		} else {
			# PASS
			return 0
		}

	} 
	# end of _el_script_exec


	# ############# MAIN ######################
	#
	#	Main Program starts here
	#	
	#
	proc main { } {
		upvar expectlite::connect_method		connect_method
		upvar expectlite::remote_host			remote_host
		upvar expectlite::_el_info_color		_el_info_color
		upvar expectlite::_el_warn_color		_el_warn_color
		upvar expectlite::_el_err_color 		_el_err_color
		upvar expectlite::delay_wait_for_host	delay_wait_for_host
		upvar expectlite::WARN					WARN
		upvar expectlite::INFO					INFO
		upvar expectlite::_el_eols				_el_eols
		upvar expectlite::_el					_el
		upvar expectlite::DEBUG_LOG 			DEBUG_LOG
		upvar expectlite::cmd_file				cmd_file


		_el_init_globals

		_el_read_args $::argc $::argv

		# Check Expect Version, warn if old
		# if { [exp_version] < "5.42.0" } {
		# 	puts "Warning: older version of expect\n"
		# }
		if [catch { exp_version 5.42.0 } msg ] {
			cputs "\nWarning: $msg\n" $_el_err_color
		}

		# check terminal type for use of colors
		check_term_type

		# Use selected (at top of file) connect_method
		switch $connect_method {
			telnet	{connect_telnet_host $remote_host }
			ssh		{connect_ssh_host 	$remote_host }
			ssh_key	{connect_ssh_key_host $remote_host }
			none		{_el_connect_localhost $remote_host }
		}


		if { $DEBUG_LOG==1 } { log_file -noappend debug_output.txt }


		# Send commands to remote host, and validate response
		set _el_result [ _el_script_exec $cmd_file ]


		after [expr 5 * $delay_wait_for_host]
		#To show the very end of the test, wait for that very last prompt
		# clear expect buffer
		expect -re {.*} {}

		# exit local or remote shell
		if { [catch {  send "exit $_el_eols" } error] } {
			if {$WARN} {cputs  "Warning: Lost connection to $remote_host \n  $error " $_el_warn_color}
		}

		wait_for_prompt

		# Print result of test
		switch -- $_el_result {
			0 { # PASS
				if {$INFO} {cputs "\n\n##Overall Result: PASS \n\n" $_el_info_color }
			}
			1 { # FAIL
				if {$INFO} {cputs "\n\n##Overall Result: FAILED (*NOFAIL on)\n\n" $_el_err_color }
				exit 1
			}
			2 { # FATAL
				if {$INFO} {cputs "\n\n##Overall Result: FAILED \n\n" $_el_err_color }
				exit 1
			}
			-1 { # UNKNOWN
				if {$INFO} {cputs "\n\n##Overall Result: UNKNOWN \n\n" $_el_err_color }
				exit 1
			}
		}


		if { $DEBUG_LOG==1 } { log_file }

		# exit with 0 to indicate that test passed
		exit 0
	}
}; #end namespace expect-lite

# run the main program (in standalone) when run with expect
#if { [info exist ::EL_LIBRARY]  != 1} { main }
if { [string match "*expect*" [info nameofexecutable]] } { expectlite::main }
