| 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" |