home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / expect / scripts / riftp < prev    next >
Encoding:
Text File  |  1992-12-29  |  9.0 KB  |  359 lines

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