Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | #!/bin/sh |
2 | # \ | |
3 | exec expect -- "$0" ${1+"$@"} | |
4 | # Name: autoexpect - generate an Expect script from watching a session | |
5 | # | |
6 | # Description: | |
7 | # | |
8 | # Given a program name, autoexpect will run that program. Otherwise | |
9 | # autoexpect will start a shell. Interact as desired. When done, exit | |
10 | # the program or shell. Autoexpect will create a script that reproduces | |
11 | # your interactions. By default, the script is named script.exp. | |
12 | # See the man page for more info. | |
13 | # | |
14 | # Author: Don Libes, NIST | |
15 | # Date: June 30 1995 | |
16 | # Version: 1.4b | |
17 | ||
18 | set filename "script.exp" | |
19 | set verbose 1 | |
20 | set conservative 0 | |
21 | set promptmode 0 | |
22 | set option_keys "" | |
23 | ||
24 | proc check_for_following {type} { | |
25 | if {![llength [uplevel set argv]]} { | |
26 | puts "autoexpect: [uplevel set flag] requires following $type" | |
27 | exit 1 | |
28 | } | |
29 | } | |
30 | ||
31 | while {[llength $argv]>0} { | |
32 | set flag [lindex $argv 0] | |
33 | if {0==[regexp "^-" $flag]} break | |
34 | set argv [lrange $argv 1 end] | |
35 | switch -- $flag \ | |
36 | "-c" { | |
37 | set conservative 1 | |
38 | } "-C" { | |
39 | check_for_following character | |
40 | lappend option_keys [lindex $argv 0] ctoggle | |
41 | set argv [lrange $argv 1 end] | |
42 | } "-p" { | |
43 | set promptmode 1 | |
44 | } "-P" { | |
45 | check_for_following character | |
46 | lappend option_keys [lindex $argv 0] ptoggle | |
47 | set argv [lrange $argv 1 end] | |
48 | } "-Q" { | |
49 | check_for_following character | |
50 | lappend option_keys [lindex $argv 0] quote | |
51 | set argv [lrange $argv 1 end] | |
52 | } "-f" { | |
53 | check_for_following filename | |
54 | set filename [lindex $argv 0] | |
55 | set argv [lrange $argv 1 end] | |
56 | } "-quiet" { | |
57 | set verbose 0 | |
58 | } default { | |
59 | break | |
60 | } | |
61 | } | |
62 | ||
63 | ############################################################# | |
64 | # Variables Descriptions | |
65 | ############################################################# | |
66 | # userbuf buffered characters from user | |
67 | # procbuf buffered characters from process | |
68 | # lastkey last key pressed by user | |
69 | # if undefined, last key came from process | |
70 | # echoing if the process is echoing | |
71 | ############################################################# | |
72 | ||
73 | # Handle a character that came from user input (i.e., the keyboard) | |
74 | proc input {c} { | |
75 | global userbuf lastkey | |
76 | ||
77 | send -- $c | |
78 | append userbuf $lastkey | |
79 | set lastkey $c | |
80 | } | |
81 | ||
82 | # Handle a null character from the keyboard | |
83 | proc input_null {} { | |
84 | global lastkey userbuf procbuf echoing | |
85 | ||
86 | send -null | |
87 | ||
88 | if {$lastkey == ""} { | |
89 | if {$echoing} { | |
90 | sendcmd "$userbuf" | |
91 | } | |
92 | if {$procbuf != ""} { | |
93 | expcmd "$procbuf" | |
94 | } | |
95 | } else { | |
96 | sendcmd "$userbuf" | |
97 | if {$echoing} { | |
98 | expcmd "$procbuf" | |
99 | sendcmd "$lastkey" | |
100 | } | |
101 | } | |
102 | cmd "send -null" | |
103 | set userbuf "" | |
104 | set procbuf "" | |
105 | set lastkey "" | |
106 | set echoing 0 | |
107 | } | |
108 | ||
109 | # Handle a character that came from the process | |
110 | proc output {s} { | |
111 | global lastkey procbuf userbuf echoing | |
112 | ||
113 | send_user -raw -- $s | |
114 | ||
115 | if {$lastkey == ""} { | |
116 | if {!$echoing} { | |
117 | append procbuf $s | |
118 | } else { | |
119 | sendcmd "$userbuf" | |
120 | expcmd "$procbuf" | |
121 | set echoing 0 | |
122 | set userbuf "" | |
123 | set procbuf $s | |
124 | } | |
125 | return | |
126 | } | |
127 | ||
128 | regexp (.)(.*) $s dummy c tail | |
129 | if {$c == $lastkey} { | |
130 | if {$echoing} { | |
131 | append userbuf $lastkey | |
132 | set lastkey "" | |
133 | } else { | |
134 | if {$procbuf != ""} { | |
135 | expcmd "$procbuf" | |
136 | set procbuf "" | |
137 | } | |
138 | set echoing 1 | |
139 | } | |
140 | append procbuf $s | |
141 | ||
142 | if {[string length $tail]} { | |
143 | sendcmd "$userbuf$lastkey" | |
144 | set userbuf "" | |
145 | set lastkey "" | |
146 | set echoing 0 | |
147 | } | |
148 | } else { | |
149 | if {!$echoing} { | |
150 | expcmd "$procbuf" | |
151 | } | |
152 | sendcmd "$userbuf$lastkey" | |
153 | set procbuf $s | |
154 | set userbuf "" | |
155 | set lastkey "" | |
156 | set echoing 0 | |
157 | } | |
158 | } | |
159 | ||
160 | # rewrite raw strings so that can appear as source code but still reproduce | |
161 | # themselves. | |
162 | proc expand {s} { | |
163 | regsub -all "\\\\" $s "\\\\\\\\" s | |
164 | regsub -all "\r" $s "\\r" s | |
165 | regsub -all "\"" $s "\\\"" s | |
166 | regsub -all "\\\[" $s "\\\[" s | |
167 | regsub -all "\\\]" $s "\\\]" s | |
168 | regsub -all "\\\$" $s "\\\$" s | |
169 | ||
170 | return $s | |
171 | } | |
172 | ||
173 | # generate an expect command | |
174 | proc expcmd {s} { | |
175 | global promptmode | |
176 | ||
177 | if {$promptmode} { | |
178 | regexp ".*\[\r\n]+(.*)" $s dummy s | |
179 | } | |
180 | ||
181 | cmd "expect -exact \"[expand $s]\"" | |
182 | } | |
183 | ||
184 | # generate a send command | |
185 | proc sendcmd {s} { | |
186 | global send_style conservative | |
187 | ||
188 | if {$conservative} { | |
189 | cmd "sleep .1" | |
190 | } | |
191 | ||
192 | cmd "send$send_style -- \"[expand $s]\"" | |
193 | } | |
194 | ||
195 | # generate any command | |
196 | proc cmd {s} { | |
197 | global fd | |
198 | puts $fd "$s" | |
199 | } | |
200 | ||
201 | proc verbose_send_user {s} { | |
202 | global verbose | |
203 | ||
204 | if {$verbose} { | |
205 | send_user -- $s | |
206 | } | |
207 | } | |
208 | ||
209 | proc ctoggle {} { | |
210 | global conservative send_style | |
211 | ||
212 | if {$conservative} { | |
213 | cmd "# conservative mode off - adding no delays" | |
214 | verbose_send_user "conservative mode off\n" | |
215 | set conservative 0 | |
216 | set send_style "" | |
217 | } else { | |
218 | cmd "# prompt mode on - adding delays" | |
219 | verbose_send_user "conservative mode on\n" | |
220 | set conservative 1 | |
221 | set send_style " -s" | |
222 | } | |
223 | } | |
224 | ||
225 | proc ptoggle {} { | |
226 | global promptmode | |
227 | ||
228 | if {$promptmode} { | |
229 | cmd "# prompt mode off - now looking for complete output" | |
230 | verbose_send_user "prompt mode off\n" | |
231 | set promptmode 0 | |
232 | } else { | |
233 | cmd "# prompt mode on - now looking only for prompts" | |
234 | verbose_send_user "prompt mode on\n" | |
235 | set promptmode 1 | |
236 | } | |
237 | } | |
238 | ||
239 | # quote the next character from the user | |
240 | proc quote {} { | |
241 | expect_user -re . | |
242 | send -- $expect_out(buffer) | |
243 | } | |
244 | ||
245 | ||
246 | if {[catch {set fd [open $filename w]} msg]} { | |
247 | puts $msg | |
248 | exit | |
249 | } | |
250 | exec chmod +x $filename | |
251 | verbose_send_user "autoexpect started, file is $filename\n" | |
252 | ||
253 | # calculate a reasonable #! line | |
254 | set expectpath /usr/local/bin ;# prepare default | |
255 | foreach dir [split $env(PATH) :] { ;# now look for real location | |
256 | if {[file executable $dir/expect] && ![file isdirectory $dir/expect]} { | |
257 | set expectpath $dir | |
258 | break | |
259 | } | |
260 | } | |
261 | ||
262 | cmd "#![set expectpath]/expect -f | |
263 | # | |
264 | # This Expect script was generated by autoexpect on [timestamp -format %c] | |
265 | # Expect and autoexpect were both written by Don Libes, NIST." | |
266 | cmd {# | |
267 | # Note that autoexpect does not guarantee a working script. It | |
268 | # necessarily has to guess about certain things. Two reasons a script | |
269 | # might fail are: | |
270 | # | |
271 | # 1) timing - A surprising number of programs (rn, ksh, zsh, telnet, | |
272 | # etc.) and devices discard or ignore keystrokes that arrive "too | |
273 | # quickly" after prompts. If you find your new script hanging up at | |
274 | # one spot, try adding a short sleep just before the previous send. | |
275 | # Setting "force_conservative" to 1 (see below) makes Expect do this | |
276 | # automatically - pausing briefly before sending each character. This | |
277 | # pacifies every program I know of. The -c flag makes the script do | |
278 | # this in the first place. The -C flag allows you to define a | |
279 | # character to toggle this mode off and on. | |
280 | ||
281 | set force_conservative 0 ;# set to 1 to force conservative mode even if | |
282 | ;# script wasn't run conservatively originally | |
283 | if {$force_conservative} { | |
284 | set send_slow {1 .1} | |
285 | proc send {ignore arg} { | |
286 | sleep .1 | |
287 | exp_send -s -- $arg | |
288 | } | |
289 | } | |
290 | ||
291 | # | |
292 | # 2) differing output - Some programs produce different output each time | |
293 | # they run. The "date" command is an obvious example. Another is | |
294 | # ftp, if it produces throughput statistics at the end of a file | |
295 | # transfer. If this causes a problem, delete these patterns or replace | |
296 | # them with wildcards. An alternative is to use the -p flag (for | |
297 | # "prompt") which makes Expect only look for the last line of output | |
298 | # (i.e., the prompt). The -P flag allows you to define a character to | |
299 | # toggle this mode off and on. | |
300 | # | |
301 | # Read the man page for more info. | |
302 | # | |
303 | # -Don | |
304 | ||
305 | } | |
306 | ||
307 | cmd "set timeout -1" | |
308 | if {$conservative} { | |
309 | set send_style " -s" | |
310 | cmd "set send_slow {1 .1}" | |
311 | } else { | |
312 | set send_style "" | |
313 | } | |
314 | ||
315 | if {[llength $argv]>0} { | |
316 | eval spawn -noecho $argv | |
317 | cmd "spawn $argv" | |
318 | } else { | |
319 | spawn -noecho $env(SHELL) | |
320 | cmd "spawn \$env(SHELL)" | |
321 | } | |
322 | ||
323 | cmd "match_max 100000" | |
324 | ||
325 | set lastkey "" | |
326 | set procbuf "" | |
327 | set userbuf "" | |
328 | set echoing 0 | |
329 | ||
330 | remove_nulls 0 | |
331 | ||
332 | eval interact $option_keys { | |
333 | -re . { | |
334 | input $interact_out(0,string) | |
335 | } -o -re .+ { | |
336 | output $interact_out(0,string) | |
337 | } eof { | |
338 | cmd "expect eof" | |
339 | return | |
340 | } | |
341 | } | |
342 | ||
343 | close $fd | |
344 | verbose_send_user "autoexpect done, file is $filename\n" |