#!/usr/bin/expect
# Copyright 2000, 2001, 2004 by Paul Mattes.
#  Permission to use, copy, modify, and distribute this software and its
#  documentation for any purpose and without fee is hereby granted,
#  provided that the above copyright notice appear in all copies and that
#  both that copyright notice and this permission notice appear in
#  supporting documentation.
#
# s3270 is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the file LICENSE for more details.
#
# Based on cms_cmd.expect licenced as above, modifications generating
# zseries-console:
# (C) Copyright IBM Corp. 2004, 2005, 2006
# and released under the GNU Public License V2
#

# Read in the glue functions.
set lib [file dirname $argv0]
source "$lib/x3270_glue.expect"

set P "console-zseries"

if { [llength $argv] != 4 } {
	puts stderr "Usage: $P <VM Userid> <VM Password> zseries <VM System>"
	exit 1
}

set username [lindex $argv 0]
set password [lindex $argv 1]
set hostname [lindex $argv 3]
set command "ipllnx"

# Procedure to wait for a READ prompt from CMS or CP.
proc waitread {} {
	Snap Save
	while {[Snap Ascii [expr [Snap Rows]-1] [expr [Snap Cols]-17] 4]
			!= "Read"} {
		Snap Wait Output
	}
}

# Procedure to check for the CMS "Ready" prompt.
# Returns its row number, or -1 for "MORE..." state, and leaves a screen with
# data to read in the Snap buffer.
proc cmd_done {} {
	global verbose
	Snap Save
	while {1} {
		if {[Snap Ascii [expr [Snap Rows]-1] [expr [Snap Cols]-20] 7]
				== "More..."} {
			if {$verbose} {puts "More..."}
			return -1
		}

		set i [expr [Snap Rows]-2]
		while {$i >= 0} {
			set text [Snap Ascii $i 0 [Snap Cols]]
			switch -regexp -- $text {
				"Ready; T=.*" {return $i}
				"Ready\(.*\); T=.*" {
					error [Snap Ascii [expr $i-1] 0 \
						[Snap Cols]]
				}
				"^ *\$" {}
				"00: Please choose*" { return $i }
				default {
					if {$verbose} {
						#puts "Incomplete $i '[string trimright $text]'"
					}
					set mylength [string length [string trimright $text]]
					if { $mylength > 5 } {
						if {[Snap Ascii $i [expr $mylength-6] 6] == "login:"} {
							puts "Hohooooooooo login prompt found"
							return $i
						}
					}
					
					set i 0
				}
			}
			incr i -1
		}
		Snap Wait Output
	}
}

# Execute a command, return the output.
proc cms_cmd {text} {
	global verbose

	# Clear the screen.
	Clear

	# Send the command.
	String "$text\n"

	# 'first' is the row where the first line of output will appear.  For
	# the first screenful it's 1; after that it's 0.
	set first 1

	# r is the result.
	set r {}

	while {1} {
		# Wait for a screenful.
		set d [cmd_done]

		# Dump out what's there.
		set i $first
		#set first 0
		if {$d < 0} {set last [expr [Snap Rows]-2]} {set last $d}

		while {$i <= $last} {
			set r [linsert $r end [string trimright [Snap Ascii $i 0 [Snap Cols]]]]
			incr i
		}
		if {$d >= 0} {
			for {set n 0} {$n < [llength $r]} {incr n} {
				puts [lindex $r $n]
			}
			break
		}

		# Clear the screen and go around again.
		Clear
	}
	return $r
}

## Calculates how many lines are there in the s3270 screen buffer
proc find_last_line {} {
	global debug
        global verbose
        Snap Save

	if {$debug == 1} {
		puts "v---"
		foreach l [Snap Ascii] {
			puts $l
		}
		puts "^---"
	}

                if {[Snap Ascii [expr [Snap Rows]-1] [expr [Snap Cols]-20] 7]
                                == "More..."} {
                        ## To denote the console buffer is full
                        return -1
                }

                set i [expr [Snap Rows]-3]

                while {$i >= 0} {
                        set text [Snap Ascii $i 0 [Snap Cols]]
                        switch -regexp -- $text {
                                "^ *\$" {
                                }
                                default {
                                        return $i
                                }
                        }
                        incr i -1
                }
                ## To denote the console buffer is empty
                return -2
}

# Print the s3270 screen buffer
proc print_cons_buf {} {
	##puts "<<output>>"
	set output 0
	while {1} {
		# Grab the screen and clear it out.
		Snap Save
		Clear

		set screen [Snap Ascii 0 0 [expr [Snap Rows]-3] [Snap Cols]]

		# Find how many lines are populated and output those.
		for {set last [llength $screen]} {$last >= 0} {incr last -1} {
			switch -regexp -- [lindex $screen $last] {
				"^ *\$" {
				}
				default {
					break
				}
			}
		}
		##puts "last<$last>"
		if {$last < 0} {
			break
		}
		for {set line 0} {$line <= $last} {incr line} {
			set output 1
			puts -nonewline "\n[string trimright [lindex $screen $line]]"
		}
	}
	##puts "output<$output>"
	return $output
}

# Set 'verbose' to 1 to get debug output from the glue functions.
set verbose 1
set debug 0

proc note {m} {
	global P
	puts "$P: $m"
}
proc warn {m} {
	global P
	puts "$P: WARNING: $m"
}
proc winge {m} {
	global P
	puts "$P: MACHINE ERROR: reboot failed - $m"
}

note "Logging into the hosting VM system to restart the VM guest"

# Start s3270
Start
# Setverbose 1

# Connect to the host and wait for an input field.
Connect $hostname
Wait InputField

# Log in and wait for CP READ or VM READ mode.
note "logging in ... sending username"
String "$username\n"

# Wait for the input field, to proceed further. If timed out screen scrape
# the s3270 (error) message.
if {[catch {set ret_val [Wait 10 InputField]} err]} {
	print_cons_buf
	note "password never prompted"
	exit 2
}

# Sometimes the input field is emitted before the output from the VMserver.
# Hence, after reading the input field wait for the output.
if {[catch {set ret_val [Wait 10 Output]} err]} {
	print_cons_buf
	note "No output seen even after the reading the InputFiled"
	exit 2
}

if { [Ascii 2 0 19 ] == "Enter your password" } {
        print_cons_buf
        note "pasword prompted ..."
        note "sending password ..."
        String "$password\n"
} elseif { [Ascii 1 11 7] == "Already"} {
        # If we can't log on, we're hosed.
        Enter 
        print_cons_buf
        warn "Already logged in. logging in here....."
        String "logon $username here\n"
	# Wait for the input field to proceed further
	if {[catch {set ret_val [Wait 10 InputField]} err]} {
		print_cons_buf
		note "password never prompted"
		exit 2
	}
        if { [Ascii 2 0 19 ] == "Enter your password"} {
                print_cons_buf
                note "sending password ..."
                String "$password\n"
        }
} elseif { [Ascii 1 20 19] == "not in CP directory" } {
        print_cons_buf
        note "'$username' is not a valid username"
        exit 1
} else { 
	# Other unknown errors (firewall??)
	print_cons_buf
	warn "Unknown Error: Check the console log"
	exit 1
}

if { [Ascii 1 11 23] == "PASSWORD NOT AUTHORIZED" } {
	while {1} {
		note "authorisation failure: PASSWORD NOT AUTHORIZED: holding ..."
		after 5000
	}
	exit 1
} elseif { [Ascii 1 11 16] == "PASSWORD EXPIRED" } {
	while {1} {
		note "authorisation failure: PASSWORD EXPIRED: holding ..."
		after 5000
	}
        note "Password expired"
	exit 1
} {
        note "Password authorized"
}
print_cons_buf
waitread

note "Connected to VM"

# If we're in CP mode, which means we disconnected last time, boot CMS.
if {[Ascii [expr [Rows]-1] [expr [Cols]-20] 2] == "Cp"} {
	note "Starting CMS"
	Clear
	String "i cms\n"
	String "no"
	waitread
}

Snap save
set n [expr [Snap Rows]-2]
set first 1
while {$n >= $first} {
	set lptext  [Snap Ascii $first 0 [Snap Cols]]
	set lplength [string length [string trimright $lptext]]
	puts "$lptext "
	incr first 
}

# Enter an empty command to get a CMS prompt.  If we don't do this, there will
# be a Ready prompt as the first line of output below.
Enter
cmd_done

note "Executing command $command"
cms_cmd $command
Clear

String "#cp term more 50 10\n"

set timeouts {1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 5}
set otimeout 0
set double 1
set cmdline {}
while {1} {
	set ctimeout $otimeout
	interact {
		"" {
			puts -nonewline "\b \b"
			set cmdline [string range $cmdline 0 end-1]
			flush stdout
		}
		"" {
			puts -nonewline "\b \b"
			set cmdline [string range $cmdline 0 end-1]
			flush stdout
		}
		"\r" {
			## When enter(alone) is pressesed, s3270 goes from
			## "Running" to "Vm Read" mode. So, for actual one
			## enter key press, two 'Enter'[a s3270 function] is 
			## required
			String $cmdline
			set cmdline {}
			Enter
			if {$double == 1} {
				Enter
			}
			set double 1

			print_cons_buf
			if {$ctimeout != 0} {
				set otimeout 0
				return
			}
		}
		-re "(.)" {
			set char $interact_out(0,string)
			puts -nonewline "$char"
			set cmdline "$cmdline$char"
			flush stdout
			set double 0

			if {$ctimeout != 0} {
				set otimeout 0
				return
			}
		}
		timeout [lindex $timeouts $otimeout] {
			## Read the console buffer and print it 
			##puts "<<timeout>>"
			if {[print_cons_buf]} {
				set otimeout 0
			} elseif {$otimeout < 15} {
				incr otimeout
			}
				
			#puts "TIMEOUT: $otimeout [lindex $timeouts $otimeout]"
			return
		}
	}
}
