# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
# 2002, 2003, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
#
# This file is part of DejaGnu.
#
# DejaGnu is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# DejaGnu 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 GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with DejaGnu; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.

# This file was written by Bob Manson (manson@cygnus.com)

#
# Open a connection to the remote DOS host.
#
proc dos_open { dest args } {
    global destbat_num
    global board_info

    if {![info exists destbat_num]} {
	set destbat_num [pid]
    }
    if { [board_info $dest conninfo] == "" } {
	set name [board_info $dest name]

	set board_info($name,conninfo) "b${destbat_num}.bat"
	incr destbat_num
    }

    if {[board_info $dest exists fileid]} {
	return [board_info $dest fileid]
    }

    verbose "doing a dos_open to $dest"

    set shell_prompt [board_info $dest shell_prompt]

    set shell_id [remote_raw_open $dest]

    if { $shell_id == "" || $shell_id < 0 } {
	return -1
    }

    if {[board_info $dest exists init_command]} {
	remote_send $dest "[board_info $dest init_command]\n"
	remote_expect $dest 10 {
	    -re "$shell_prompt" { }
	    default {
		perror "failed connection to DOS on $dest."
		return -1
	    }
	}
    }

    if {[board_info $dest exists ftp_directory]} {
	set dir [board_info $dest ftp_directory]
	regsub -all "/" "$dir" "\\" dir
	remote_send $dest "cd $dir\n"
	remote_expect $dest 10 {
	    -re "$shell_prompt" { }
	    default {
		perror "failed connection to DOS on $dest."
		return -1
	    }
	}
    }

    if {[board_info $dest exists dos_dir]} {
	set dos_dir [board_info $dest dos_dir]
	regsub -all "^(\[a-zA-Z]:).*$" "$dos_dir" "\\1" drive
	regsub -all "^\[a-zA-Z]:" "$dos_dir" "" dos_dir
	remote_send $dest "${drive}\n"
	remote_expect $dest 10 {
	    -re "$shell_prompt" { }
	    default {
		perror "failed connection to DOS on $dest."
		return -1
	    }
	}
	remote_send $dest "cd $dos_dir\n"
	remote_expect $dest 10 {
	    -re "$shell_prompt" { }
	    default {
		perror "failed connection to DOS on $dest."
		return -1
	    }
	}
    }

    global target_alias
    if {[info exists target_alias]} {
	set talias $target_alias
    } else {
	set talias "foo-bar"
    }

    if {[board_info $dest exists name]} {
	set n [board_info $dest name]
    } else {
	set n $dest
    }
    set board_info($n,fileid) $shell_id

    if {[board_info $dest exists init_script]} {
	remote_exec $dest "[board_info $dest init_script] $talias"
    }

    verbose "Succeeded in connecting to DOS."
    return $shell_id
}

#
# Close the connection to the remote host. If we're telnetting there, we
# need to exit the connection first (ataman telnetd gets confused otherwise).
#
proc dos_close { dest args } {
    if {[board_info $dest exists fileid]} {
	if { [board_info $dest connect] == "telnet" } {
	    remote_send $dest "exit\n"
	    sleep 2
	}
	return [remote_raw_close $dest]
    }
}

proc dos_prep_command { dest cmdline } {
    global board_info

    set name [board_info $dest name]
    set shell_id [remote_open "$dest"]

    set localbat "/tmp/b[pid].bat"
    set remotebat [board_info $dest conninfo]

    verbose "opened"
    if { $shell_id != "" && $shell_id >= 0 } {
	set fileid [open "$localbat" "w"]
	puts -nonewline $fileid "@echo off\r\n$cmdline\r\nif errorlevel 1 echo *** DOSEXIT code 1\r\nif not errorlevel 1 echo *** DOSEXIT code 0\r\n"
	close $fileid
	set result [remote_download $dest $localbat $remotebat]
    } else {
	set result ""
    }
    remote_file build delete $localbat
    return $result
}

#
# Run CMDLINE on DESTHOST. We handle two cases; one is where we're at
# a DOS prompt, and the other is where we're in GDB.
# We run CMDLINE by creating a batchfile, downloading it, and then
# executing it; this handles the case where the commandline is too
# long for command.com to deal with.
#

proc dos_exec { dest program pargs inp outp } {
    set cmdline "$program $pargs"

    set shell_prompt [board_info $dest shell_prompt]

    if { $inp != "" } {
	set inp [remote_download $dest $inp inpfile]
	if { $inp != "" } {
	    set inp " < $inp"
	}
    }

    if { $outp != "" } {
	set outpf " > tempout"
    }  else {
	set outpf ""
    }

    verbose "cmdline is $cmdline$inp." 2

    # Make a DOS batch file; we use @echo off so we don't have to see
    # the DOS command prompts and such.
    for { set i 0 } { $i < 2 } { incr i } {
	set exit_status -1
	verbose "calling open"
	set batfile [dos_prep_command $dest "$cmdline$inp$outpf"]
	if { $batfile != "" } {
	    if { [dos_start_command $batfile $dest] == "" } {
		# FIXME: The 300 below should be a parameter.
		set result [remote_wait $dest 300]
		set exit_status [lindex $result 0]
		set output [lindex $result 1]
	    }
	}
	if { $exit_status >= 0 } {
	    if { $outp != "" } {
		remote_upload $dest tempout $outp
		remote_file $dest delete tempout
	    }
	    return [list $exit_status $output]
	}
	if { $exit_status != -2 } {
	    remote_close $dest
	    remote_reboot $dest
	}
    }
    return [list -1 "program execution failed"]
}

#
# Start CMDLINE executing on DEST.
# There are two cases that we handle, one where we're at a DOS prompt
# and the other is when the remote machine is running GDB.
#

proc dos_start_command { cmdline dest } {
    set shell_prompt [board_info $dest shell_prompt]
    set prefix ""
    set ok 0
    for { set i 0 } {$i <= 2 && ! $ok} { incr i } {
	set shell_id [remote_open $dest]
	if { $shell_id != "" && $shell_id > 0 } {
	    remote_send $dest "echo k\r"
	    remote_expect $dest 20 {
		-re "\\(gdb\\)" {
		    set shell_prompt "\\(gdb\\)"
		    # gdb uses 'shell command'.
		    set prefix "shell "
		    set ok 1
		}
		-re "$shell_prompt" {
		    set ok 1
		}
		default { }
	    }
	}
	if { ! $ok } {
	    remote_close $dest
	    remote_reboot $dest
	}
    }
    if { ! $ok } {
	return "unable to start command"
    } else {
	remote_send $dest "${prefix}${cmdline}\n"
	remote_expect $dest 2 {
	    -re "${cmdline}\[\r\n\]\[\r\n\]?" { }
	    timeout { }
	}
	return ""
    }
}

#
# Send STRING to DEST, translating all LFs to CRs first, and sending one
# line at a time because of strangeness with telnet in some circumstances.
#

proc dos_send { dest string } {
    verbose "Sending '$string' to $dest" 2
    # Convert LFs to CRs, 'cause that is what DOS wants to see.
    set first 1
    set string [string trimright $string "\r\n"]
    foreach line [split $string "\r\n"] {
	if {$first} {
	    set first 0
	} else {
	    # small delay between lines, to keep from
	    # overwhelming the stupid telnet server.
	    sleep 1.0
	}
	remote_raw_send $dest "$line\r"
    }
}

#
# Spawn PROGRAM on DEST, and return the spawn_id associated with the
# connection; we can only spawn one command at a time.
#

proc dos_spawn { dest program args } {
    verbose "running $program on $dest"
    set remotebat [dos_prep_command $dest $program]

    for { set x 0 } { $x < 3 } { incr x } {
	if { [dos_start_command $remotebat $dest] == "" } {
	    return [board_info $dest fileid]
	}
	remote_close $dest
	remote_reboot $dest
    }
    return -1
}

proc dos_wait { dest timeout } {
    set output ""
    set shell_prompt [board_info $dest shell_prompt]
    set status 1

    verbose "waiting in dos_wait"
    remote_expect $dest $timeout {
	-re "(.*)\[*\]\[*\]\[*\] DOSEXIT code (\[0-9\]+)\[\r\n\]\[\r\n\]?" {
	    verbose "got exit status"
	    append output $expect_out(1,string)
	    set status $expect_out(2,string)
	    exp_continue
	}

	-re "(.*)${shell_prompt}" {
	    append output $expect_out(1,string)
	    verbose "output from dos is:'$output'"
	    return [list $status $output]
	}

	-re "(.*)\\(gdb\\)" {
	    append output $expect_out(1,string)
	    return [list $status $output]
	}

	-re "In.*cygwin.*except" {
	    remote_close $dest
	    remote_reboot $dest
	    return [list -2 $output]
	}

	-re "\[\r\n\]+" {
	    # This is a bit obscure. We only want to put whole
	    # lines into the output string, because otherwise we
	    # might miss a prompt because we only got 1/2 of it the
	    # first time 'round. The other tricky bit is that
	    # expect_out(buffer) will contain everything before and including
	    # the matched pattern.
	    append output $expect_out(buffer)
	    exp_continue -continue_timer
	}

	timeout {
	    warning "timeout in dos_wait"
	    if { [dos_interrupt_job $dest] == "" } {
		return [list 1 $output]
	    }
	}

	eof {
	    warning "got EOF from dos host."
	}
    }

    remote_close $dest

    return [list -1 $output]
}

proc dos_load { dest prog args } {
    global dos_dll_loaded
    set progargs ""
    set inpfile ""
    if { [llength $args] > 0 } {
	set progargs [lindex $args 1]
    }
    if { [llength $args] > 1 } {
	set inpfile [lindex $args 1]
    }
    if {![info exists dos_dll_loaded]} {
	if {![is_remote host]} {
	    global target_alias

	    set comp [get_multilibs]
	    if {[file exists [file join ${comp} winsup new-cygwin1.dll]]} {
		set dll "${comp}/winsup/new-cygwin1.dll"
		set dll_name "cygwin1.dll"
	    } elseif {[file exists [file join ${comp} winsup new-cygwin.dll]]} {
		set dll "${comp}/winsup/new-cygwin.dll"
		set dll_name "cygwin.dll"
	    } elseif {[file exists [file join ${comp} lib cygwin1.dll]]} {
		set dll "${comp}/lib/cygwin1.dll"
		set dll_name "cygwin1.dll"
	    } elseif {[file exists [file join ${comp} lib cygwin.dll]]} {
		set dll "${comp}/lib/cygwin.dll"
		set dll_name "cygwin.dll"
	    } else {
		error "couldn't find cygwin.dll:$comp"
		return "fail"
	    }
	    remote_download $dest $dll $dll_name
	}
	set dos_dll_loaded 1
    }
    set remote_prog [remote_download $dest $prog "aout.exe"]
    set result [remote_exec $dest $remote_prog $progargs $inpfile]
    set status [lindex $result 0]
    set output [lindex $result 1]
    set status2 [check_for_board_status output]
    if { $status2 >= 0 } {
	set status $status2
    }
    if { $status != 0 } {
	set status "fail"
    } else {
	set status "pass"
    }
    return [list $status $output]
}

proc dos_file { dest op args } {
    switch $op {
	delete {
	    foreach x $args {
		remote_exec $dest "del" "$x"
	    }
	    return

	}
	default {
	    return [eval standard_file \{$dest\} \{$op\} $args]
	}
    }
}

#
# Interrupt the current spawned command being run; the only tricky
# part is that we have to handle the "Terminate batch job" prompt.
#
proc dos_interrupt_job { host } {
    set shell_prompt [board_info $host shell_prompt]

    remote_send $host "\003"
    remote_expect $host 10 {
	-re "Terminate batch job.*Y/N\[)\]\[?\] *$" {
	    remote_send $host "n\n"
	    exp_continue
	}
	-re "$shell_prompt" {
	    return ""
	}
	-re ">" {
	    remote_send $host "\n"
	    exp_continue
	}
    }
    return "fail"
}

proc dos_copy_download { host localfile remotefile } {
    remote_file build delete "[board_info $host local_dir]/$remotefile"
    if {[remote_file build exists $localfile]} {
	set result [remote_download build $localfile "[board_info $host local_dir]/$remotefile"]
	if { $result != "" } {
	    remote_exec build "chmod" "a+rw $result"
	    return $remotefile
	}
    } else {
	return ""
    }
}

proc dos_copy_upload { host remotefile localfile } {
    remote_file build delete $localfile
    if {[file exists [file join [board_info $host local_dir] $remotefile]]} {
	set result [remote_download build "[board_info $host local_dir]/$remotefile" $localfile]
    } else {
	set result ""
    }
    if { $result != "" } {
	remote_exec build "chmod" "a+rw $result"
	return $result
    }
}

proc dos_copy_file { dest op args } {
    if { $op == "delete" } {
	set file "[board_info $dest local_dir]/[lindex $args 0]"
	remote_file build delete $file
    }
}

set_board_info protocol  "dos"
set_board_info shell_prompt  "(^|\[\r\n\])\[a-zA-Z\]:\[^\r\n\]*>\[ \t\]*$"
set_board_info needs_status_wrapper 1
