1# Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Free Software
2# Foundation, Inc.
3#
4# This file is part of DejaGnu.
5#
6# DejaGnu is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# DejaGnu is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14# General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with DejaGnu; if not, write to the Free Software Foundation,
18# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20# This file was written by Bob Manson (manson@cygnus.com)
21
22#
23# Open a connection to the remote DOS host.
24#
25proc dos_open { dest args } {
26    global destbat_num
27
28    if ![info exists destbat_num] {
29	set destbat_num [pid]
30    }
31    if { [board_info $dest conninfo] == "" } {
32	global board_info
33	set name [board_info $dest name]
34
35	set board_info($name,conninfo) "b${destbat_num}.bat"
36	incr destbat_num
37    }
38
39    if [board_info $dest exists fileid] {
40	return [board_info $dest fileid]
41    }
42
43    verbose "doing a dos_open to $dest"
44
45    set shell_prompt [board_info $dest shell_prompt]
46
47    set shell_id [remote_raw_open $dest]
48
49    if { $shell_id == "" || $shell_id < 0 } {
50	return -1
51    }
52
53    if [board_info $dest exists init_command] {
54	remote_send $dest "[board_info $dest init_command]\n"
55	remote_expect $dest 10 {
56	    -re "$shell_prompt" { }
57	    default {
58		perror "failed connection to DOS on $dest."
59		return -1
60	    }
61	}
62    }
63
64    if [board_info $dest exists ftp_directory] {
65	set dir [board_info $dest ftp_directory]
66	regsub -all "/" "$dir" "\\" dir
67	remote_send $dest "cd $dir\n"
68	remote_expect $dest 10 {
69	    -re "$shell_prompt" { }
70	    default {
71		perror "failed connection to DOS on $dest."
72		return -1
73	    }
74	}
75    }
76
77    if [board_info $dest exists dos_dir] {
78	set dos_dir [board_info $dest dos_dir]
79	regsub -all "^(\[a-zA-Z]:).*$" "$dos_dir" "\\1" drive
80	regsub -all "^\[a-zA-Z]:" "$dos_dir" "" dos_dir
81	remote_send $dest "${drive}\n"
82	remote_expect $dest 10 {
83	    -re "$shell_prompt" { }
84	    default {
85		perror "failed connection to DOS on $dest."
86		return -1
87	    }
88	}
89	remote_send $dest "cd $dos_dir\n"
90	remote_expect $dest 10 {
91	    -re "$shell_prompt" { }
92	    default {
93		perror "failed connection to DOS on $dest."
94		return -1
95	    }
96	}
97    }
98
99    global target_alias
100    if [info exists target_alias] {
101	set talias $target_alias
102    } else {
103	set talias "foo-bar"
104    }
105
106    global board_info
107    if [board_info $dest exists name] {
108	set n [board_info $dest name]
109    } else {
110	set n $dest
111    }
112    set board_info($n,fileid) $shell_id
113
114    if [board_info $dest exists init_script] {
115	remote_exec $dest "[board_info $dest init_script] $talias"
116    }
117
118    verbose "Succeeded in connecting to DOS."
119    return $shell_id
120}
121
122#
123# Close the connection to the remote host. If we're telnetting there, we
124# need to exit the connection first (ataman telnetd gets confused otherwise).
125#
126proc dos_close { dest args } {
127    if [board_info $dest exists fileid] {
128	if { [board_info $dest connect] == "telnet" } {
129	    remote_send $dest "exit\n"
130	    sleep 2
131	}
132	return [remote_raw_close $dest]
133    }
134}
135
136proc dos_prep_command { dest cmdline } {
137    global board_info
138
139    set name [board_info $dest name]
140    set shell_id [remote_open "$dest"]
141
142    set localbat "/tmp/b[pid].bat"
143    set remotebat [board_info $dest conninfo]
144
145    verbose "opened"
146    if { $shell_id != "" && $shell_id >= 0 } {
147	set fileid [open "$localbat" "w"]
148	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"
149	close $fileid
150	set result [remote_download $dest $localbat $remotebat]
151    } else {
152	set result ""
153    }
154    remote_file build delete $localbat
155    return $result
156}
157
158#
159# Run CMDLINE on DESTHOST. We handle two cases; one is where we're at
160# a DOS prompt, and the other is where we're in GDB.
161# We run CMDLINE by creating a batchfile, downloading it, and then
162# executing it; this handles the case where the commandline is too
163# long for command.com to deal with.
164#
165
166proc dos_exec { dest program pargs inp outp } {
167    set cmdline "$program $pargs"
168
169    set shell_prompt [board_info $dest shell_prompt]
170
171    if { $inp != "" } {
172	set inp [remote_download $dest $inp inpfile]
173	if { $inp != "" } {
174	    set inp " < $inp"
175	}
176    }
177
178    if { $outp != "" } {
179	set outpf " > tempout"
180    }  else {
181	set outpf ""
182    }
183
184    verbose "cmdline is $cmdline$inp." 2
185
186    # Make a DOS batch file; we use @echo off so we don't have to see
187    # the DOS command prompts and such.
188    for { set i 0 } { $i < 2 } { incr i } {
189	set exit_status -1
190	verbose "calling open"
191	set batfile [dos_prep_command $dest "$cmdline$inp$outpf"]
192	if { $batfile != "" } {
193	    if { [dos_start_command $batfile $dest] == "" } {
194		# FIXME: The 300 below should be a parameter.
195		set result [remote_wait $dest 300]
196		set exit_status [lindex $result 0]
197		set output [lindex $result 1]
198	    }
199	}
200	if { $exit_status >= 0 } {
201	    if { $outp != "" } {
202		remote_upload $dest tempout $outp
203		remote_file $dest delete tempout
204	    }
205	    return [list $exit_status $output]
206	}
207	if { $exit_status != -2 } {
208	    remote_close $dest
209	    remote_reboot $dest
210	}
211    }
212    return [list -1 "program execution failed"]
213}
214
215#
216# Start CMDLINE executing on DEST.
217# There are two cases that we handle, one where we're at a DOS prompt
218# and the other is when the remote machine is running GDB.
219#
220
221proc dos_start_command { cmdline dest } {
222    set shell_prompt [board_info $dest shell_prompt]
223    set prefix ""
224    set ok 0
225    for { set i 0 } {$i <= 2 && ! $ok} { incr i } {
226	set shell_id [remote_open $dest]
227	if { $shell_id != "" && $shell_id > 0 } {
228	    remote_send $dest "echo k\r"
229	    remote_expect $dest 20 {
230		-re "\\(gdb\\)" {
231		    set shell_prompt "\\(gdb\\)"
232		    # gdb uses 'shell command'.
233		    set prefix "shell "
234		    set ok 1
235		}
236		-re "$shell_prompt" {
237		    set ok 1
238		}
239		default { }
240	    }
241	}
242	if { ! $ok } {
243	    remote_close $dest
244	    remote_reboot $dest
245	}
246    }
247    if { ! $ok } {
248	return "unable to start command"
249    } else {
250	remote_send $dest "${prefix}${cmdline}\n"
251	remote_expect $dest 2 {
252	    -re "${cmdline}\[\r\n\]\[\r\n\]?" { }
253	    timeout { }
254	}
255	return ""
256    }
257}
258
259#
260# Send STRING to DEST, translating all LFs to CRs first, and sending one
261# line at a time because of strangeness with telnet in some circumstances.
262#
263
264proc dos_send { dest string } {
265    verbose "Sending '$string' to $dest" 2
266    # Convert LFs to CRs, 'cause that is what DOS wants to see.
267    set first 1
268    set string [string trimright $string "\r\n"]
269    foreach line [split $string "\r\n"] {
270	if {$first} {
271	    set first 0
272	} else {
273	    # small delay between lines, to keep from
274	    # overwhelming the stupid telnet server.
275	    sleep 1.0
276	}
277	remote_raw_send $dest "$line\r"
278    }
279}
280
281#
282# Spawn PROGRAM on DEST, and return the spawn_id associated with the
283# connection; we can only spawn one command at a time.
284#
285
286proc dos_spawn { dest program args } {
287    verbose "running $program on $dest"
288    set remotebat [dos_prep_command $dest $program]
289
290    for { set x 0 } { $x < 3 } { incr x } {
291	if { [dos_start_command $remotebat $dest] == "" } {
292	    return [board_info $dest fileid]
293	}
294	remote_close $dest
295	remote_reboot $dest
296    }
297    return -1
298}
299
300proc dos_wait { dest timeout } {
301    set output ""
302    set shell_prompt [board_info $dest shell_prompt]
303    set status 1
304
305    verbose "waiting in dos_wait"
306    remote_expect $dest $timeout {
307	-re "(.*)\[*\]\[*\]\[*\] DOSEXIT code (\[0-9\]+)\[\r\n\]\[\r\n\]?" {
308	    verbose "got exit status"
309	    append output $expect_out(1,string)
310	    set status $expect_out(2,string)
311	    exp_continue
312	}
313
314	-re "(.*)${shell_prompt}" {
315	    append output $expect_out(1,string)
316	    verbose "output from dos is:'$output'"
317	    return [list $status $output]
318	}
319
320	-re "(.*)\\(gdb\\)" {
321	    append output $expect_out(1,string)
322	    return [list $status $output]
323	}
324
325	-re "In.*cygwin.*except" {
326	    remote_close $dest
327	    remote_reboot $dest
328	    return [list -2 $output]
329	}
330
331	-re "\[\r\n\]+" {
332	    # This is a bit obscure. We only want to put whole
333	    # lines into the output string, because otherwise we
334	    # might miss a prompt because we only got 1/2 of it the
335	    # first time 'round. The other tricky bit is that
336	    # expect_out(buffer) will contain everything before and including
337	    # the matched pattern.
338	    append output $expect_out(buffer)
339	    exp_continue -continue_timer
340	}
341
342	timeout {
343	    warning "timeout in dos_wait"
344	    if { [dos_interrupt_job $dest] == "" } {
345		return [list 1 $output]
346	    }
347	}
348
349	eof {
350	    warning "got EOF from dos host."
351	}
352    }
353
354    remote_close $dest
355
356    return [list -1 $output]
357}
358
359proc dos_load { dest prog args } {
360    global dos_dll_loaded
361    set progargs ""
362    set inpfile ""
363    if { [llength $args] > 0 } {
364	set progargs [lindex $args 1]
365    }
366    if { [llength $args] > 1 } {
367	set inpfile [lindex $args 1]
368    }
369    if ![info exists dos_dll_loaded] {
370	if ![is_remote host] {
371	    global target_alias
372
373	    set comp [get_multilibs]
374	    if [file exists "${comp}/winsup/new-cygwin1.dll"] {
375		set dll "${comp}/winsup/new-cygwin1.dll"
376		set dll_name "cygwin1.dll"
377	    } elseif [file exists "${comp}/winsup/new-cygwin.dll"] {
378		set dll "${comp}/winsup/new-cygwin.dll"
379		set dll_name "cygwin.dll"
380	    } elseif [file exists ${comp}/lib/cygwin1.dll] {
381		set dll "${comp}/lib/cygwin1.dll"
382		set dll_name "cygwin1.dll"
383	    } elseif [file exists ${comp}/lib/cygwin.dll] {
384		set dll "${comp}/lib/cygwin.dll"
385		set dll_name "cygwin.dll"
386	    } else {
387		error "couldn't find cygwin.dll:$comp"
388		return "fail"
389	    }
390	    remote_download $dest $dll $dll_name
391	}
392	set dos_dll_loaded 1
393    }
394    set remote_prog [remote_download $dest $prog "aout.exe"]
395    set result [remote_exec $dest $remote_prog $progargs $inpfile]
396    set status [lindex $result 0]
397    set output [lindex $result 1]
398    set status2 [check_for_board_status output]
399    if { $status2 >= 0 } {
400	set status $status2
401    }
402    if { $status != 0 } {
403	set status "fail"
404    } else {
405	set status "pass"
406    }
407    return [list $status $output]
408}
409
410proc dos_file { dest op args } {
411    switch $op {
412	delete {
413	    foreach x $args {
414		remote_exec $dest "del" "$x"
415	    }
416	    return
417
418	}
419	default {
420	    return [eval standard_file \{$dest\} \{$op\} $args]
421	}
422    }
423}
424
425#
426# Interrupt the current spawned command being run; the only tricky
427# part is that we have to handle the "Terminate batch job" prompt.
428#
429proc dos_interrupt_job { host } {
430    set shell_prompt [board_info $host shell_prompt]
431
432    remote_send $host "\003"
433    remote_expect $host 10 {
434	-re "Terminate batch job.*Y/N\[)\]\[?\] *$" {
435	    remote_send $host "n\n"
436	    exp_continue
437	}
438	-re "$shell_prompt" {
439	    return ""
440	}
441	-re ">" {
442	    remote_send $host "\n"
443	    exp_continue
444	}
445    }
446    return "fail"
447}
448
449proc dos_copy_download { host localfile remotefile } {
450    remote_file build delete "[board_info $host local_dir]/$remotefile"
451    if [remote_file build exists $localfile] {
452	set result [remote_download build $localfile "[board_info $host local_dir]/$remotefile"]
453	if { $result != "" } {
454	    remote_exec build "chmod" "a+rw $result"
455	    return $remotefile
456	}
457    } else {
458	return ""
459    }
460}
461
462proc dos_copy_upload { host remotefile localfile } {
463    remote_file build delete $localfile
464    if [file exists "[board_info $host local_dir]/$remotefile"] {
465	set result [remote_download build "[board_info $host local_dir]/$remotefile" $localfile]
466    } else {
467	set result ""
468    }
469    if { $result != "" } {
470	remote_exec build "chmod" "a+rw $result"
471	return $result
472    }
473}
474
475proc dos_copy_file { dest op args } {
476    if { $op == "delete" } {
477	set file "[board_info $dest local_dir]/[lindex $args 0]"
478	remote_file build delete $file
479    }
480}
481
482set_board_info protocol  "dos"
483set_board_info shell_prompt  "(^|\[\r\n\])\[a-zA-Z\]:\[^\r\n\]*>\[ \t\]*$"
484set_board_info needs_status_wrapper 1
485