/usr/bin/expect_rftp is in expect 5.45-7.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | #!/bin/sh
# -*- tcl -*-
# The next line is executed by /bin/sh, but not tcl \
exec tclsh8.6 "$0" ${1+"$@"}
package require Expect
# rftp - ftp a directory hierarchy (i.e. recursive ftp)
# Version 2.10
# Don Libes, NIST
exp_version -exit 5.0
# rftp is much like ftp except that the command ~g copies everything in
# the remote current working directory to the local current working
# directory. Similarly ~p copies in the reverse direction. ~l just
# lists the remote directories.
# rftp takes an argument of the host to ftp to. Username and password
# are prompted for. Other ftp options can be set interactively at that
# time. If your local ftp understands .netrc, that is also used.
# ~/.rftprc is sourced after the user has logged in to the remote site
# and other ftp commands may be sent at that time. .rftprc may also be
# used to override the following rftp defaults. The lines should use
# the same syntax as these:
set file_timeout 3600 ;# timeout (seconds) for retrieving files
set timeout 1000000 ;# timeout (seconds) for other ftp dialogue
set default_type binary ;# default type, i.e., ascii, binary, tenex
set binary {} ;# files matching are transferred as binary
set ascii {} ;# as above, but as ascii
set tenex {} ;# as above, but as tenex
# The values of binary, ascii and tenex should be a list of (Tcl) regular
# expressions. For example, the following definitions would force files
# ending in *.Z and *.tar to be transferred as binaries and everything else
# as text.
# set default_type ascii
# set binary {*.Z *.tar}
# If you are on a UNIX machine, you can probably safely ignore all of this
# and transfer everything as "binary".
# The current implementation requires that the source host be able to
# provide directory listings in UNIX format. Hence, you cannot copy
# from a VMS host (although you can copy to it). In fact, there is no
# standard for the output that ftp produces, and thus, ftps that differ
# significantly from the ubiquitous UNIX implementation may not work
# with rftp (at least, not without changing the scanning and parsing).
####################end of documentation###############################
match_max -d 100000 ;# max size of a directory listing
# return name of file from one line of directory listing
proc getname {line} {
# if it's a symbolic link, return local name
set i [lsearch $line "->"]
if {-1==$i} {
# not a sym link, return last token of line as name
return [lindex $line [expr [llength $line]-1]]
} else {
# sym link, return "a" of "a -> b"
return [lindex $line [expr $i-1]]
}
}
proc putfile {name} {
global current_type default_type
global binary ascii tenex
global file_timeout
switch -- $name $binary {set new_type binary} \
$ascii {set new_type ascii} \
$tenex {set new_type tenex} \
default {set new_type $default_type}
if {$current_type != $new_type} {
settype $new_type
}
set timeout $file_timeout
send "put $name\r"
expect timeout {
send_user "ftp timed out in response to \"put $name\"\n"
exit
} "ftp>*"
}
proc getfile {name} {
global current_type default_type
global binary ascii tenex
global file_timeout
switch -- $name $binary {set new_type binary} \
$ascii {set new_type ascii} \
$tenex {set new_type tenex} \
default {set new_type $default_type}
if {$current_type != $new_type} {
settype $new_type
}
set timeout $file_timeout
send "get $name\r"
expect timeout {
send_user "ftp timed out in response to \"get $name\"\n"
exit
} "ftp>*"
}
# returns 1 if successful, 0 otherwise
proc putdirectory {name} {
send "mkdir $name\r"
expect "550*denied*ftp>*" {
send_user "failed to make remote directory $name\n"
return 0
} timeout {
send_user "timed out on make remote directory $name\n"
return 0
} -re "(257|550.*exists).*ftp>.*"
# 550 is returned if directory already exists
send "cd $name\r"
expect "550*ftp>*" {
send_user "failed to cd to remote directory $name\n"
return 0
} timeout {
send_user "timed out on cd to remote directory $name\n"
return 0
} -re "2(5|0)0.*ftp>.*"
# some ftp's return 200, some return 250
send "lcd $name\r"
# hard to know what to look for, since my ftp doesn't return status
# codes. It is evidentally very locale-dependent.
# So, assume success.
expect "ftp>*"
putcurdirectory
send "lcd ..\r"
expect "ftp>*"
send "cd ..\r"
expect timeout {
send_user "failed to cd to remote directory ..\n"
return 0
} -re "2(5|0)0.*ftp>.*"
return 1
}
# returns 1 if successful, 0 otherwise
proc getdirectory {name transfer} {
send "cd $name\r"
# this can fail normally if it's a symbolic link, and we are just
# experimenting
expect "550*$name*ftp>*" {
send_user "failed to cd to remote directory $name\n"
return 0
} timeout {
send_user "timed out on cd to remote directory $name\n"
return 0
} -re "2(5|0)0.*ftp>.*"
# some ftp's return 200, some return 250
if {$transfer} {
send "!mkdir $name\r"
expect "denied*" return timeout return "ftp>"
send "lcd $name\r"
# hard to know what to look for, since my ftp doesn't return
# status codes. It is evidentally very locale-dependent.
# So, assume success.
expect "ftp>*"
}
getcurdirectory $transfer
if {$transfer} {
send "lcd ..\r"
expect "ftp>*"
}
send "cd ..\r"
expect timeout {
send_user "failed to cd to remote directory ..\n"
return 0
} -re "2(5|0)0.*ftp>.*"
return 1
}
proc putentry {name type} {
switch -- $type d {
# directory
if {$name=="." || $name==".."} return
putdirectory $name
} - {
# file
putfile $name
} l {
# symlink, could be either file or directory
# first assume it's a directory
if {[putdirectory $name]} return
putfile $name
} default {
send_user "can't figure out what $name is, skipping\n"
}
}
proc getentry {name type transfer} {
switch -- $type d {
# directory
if {$name=="." || $name==".."} return
getdirectory $name $transfer
} - {
# file
if {!$transfer} return
getfile $name
} l {
# symlink, could be either file or directory
# first assume it's a directory
if {[getdirectory $name $transfer]} return
if {!$transfer} return
getfile $name
} default {
send_user "can't figure out what $name is, skipping\n"
}
}
proc putcurdirectory {} {
send "!/bin/ls -alg\r"
expect timeout {
send_user "failed to get directory listing\n"
return
} "ftp>*"
set buf $expect_out(buffer)
while {1} {
# if end of listing, succeeded!
if 0==[regexp "(\[^\n]*)\n(.*)" $buf dummy line buf] return
set token [lindex $line 0]
switch -- $token !/bin/ls {
# original command
} total {
# directory header
} . {
# unreadable
} default {
# either file or directory
set name [getname $line]
set type [string index $line 0]
putentry $name $type
}
}
}
# look at result of "dir". If transfer==1, get all files and directories
proc getcurdirectory {transfer} {
send "dir\r"
expect timeout {
send_user "failed to get directory listing\n"
return
} "ftp>*"
set buf $expect_out(buffer)
while {1} {
regexp "(\[^\n]*)\n(.*)" $buf dummy line buf
set token [lindex $line 0]
switch -- $token dir {
# original command
} 200 {
# command successful
} 150 {
# opening data connection
} total {
# directory header
} 226 {
# transfer complete, succeeded!
return
} ftp>* {
# next prompt, failed!
return
} . {
# unreadable
} default {
# either file or directory
set name [getname $line]
set type [string index $line 0]
getentry $name $type $transfer
}
}
}
proc settype {t} {
global current_type
send "type $t\r"
set current_type $t
expect "200*ftp>*"
}
proc final_msg {} {
# write over the previous prompt with our message
send_user "\rQuit ftp or cd to another directory and press ~g, ~p, or ~l\n"
# and then reprompt
send_user "ftp> "
}
if {[file readable ~/.rftprc]} {source ~/.rftprc}
set first_time 1
if {$argc>1} {
send_user "usage: rftp [host]"
exit
}
send_user "Once logged in, cd to the directory to be transferred and press:\n"
send_user "~p to put the current directory from the local to the remote host\n"
send_user "~g to get the current directory from the remote host to the local host\n"
send_user "~l to list the current directory from the remote host\n"
if {$argc==0} {spawn ftp} else {spawn ftp $argv}
interact -echo ~g {
if {$first_time} {
set first_time 0
settype $default_type
}
getcurdirectory 1
final_msg
} -echo ~p {
if {$first_time} {
set first_time 0
settype $default_type
}
putcurdirectory
final_msg
} -echo ~l {
getcurdirectory 0
final_msg
}
|