merlyn@iwarp.intel.com (Randal L. Schwartz) (05/01/91)
Here's an improved version of the chat2.pl script that supports opening a TCP socket awaiting a connection (giving you enough info to tell the other end what to do). And, as if that wasn't enough, I've also enclosed ftp-ls-R, which acts as a rudimentary ftp client by connecting to an ftp server then performing an "ls -Ra" equivalent. Usage is something like: ftp-ls-R host [topdir] so an invocation of ftp-ls-R prep.ai.mit.edu pub/gnu gives you all the GNU stuff. Enjoy. I'm still hacking away. I hope to expand this ftp client to maintain ftp-shadow directories with minimal fuss (have all the RFCs online here, for example). If someone else does something with this, let me know, so I can coordinate. #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: chat2.pl ftp-ls-R # Wrapped by merlyn@iwarpse on Tue Apr 30 13:28:42 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'chat2.pl' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'chat2.pl'\" else echo shar: Extracting \"'chat2.pl'\" \(7365 characters\) sed "s/^X//" >'chat2.pl' <<'END_OF_FILE' X## chat.pl: chat with a server X## V2.01.alpha.3 91/04/30 X## Randal L. Schwartz X Xpackage chat; X X$sockaddr = 'S n a4 x8'; Xchop($thishost = `hostname`); $thisaddr = (gethostbyname($thishost))[4]; X$thisproc = pack($sockaddr, 2, 0, $thisaddr); X X# *S = symbol for current I/O, gets assigned *chatsymbol.... X$next = "chatsymbol000000"; # next one X$nextpat = "^chatsymbol"; # patterns that match next++, ++, ++, ++ X X X## $handle = &chat'open_port("server.address",$port_number); X## opens a named or numbered TCP server X Xsub open_port { ## public X local($server, $port) = @_; X X local($serveraddr,$serverproc); X X *S = ++$next; X if ($server =~ /^(\d+)+\.(\d+)\.(\d+)\.(\d+)$/) { X $serveraddr = pack('C4', $1, $2, $3, $4); X } else { X local(@x) = gethostbyname($server); X return undef unless @x; X $serveraddr = $x[4]; X } X $serverproc = pack($sockaddr, 2, $port, $serveraddr); X unless (socket(S, 2, 1, 6)) { X # XXX hardwired $AF_SOCKET, $SOCK_STREAM, 'tcp' X # but who the heck would change these anyway? (:-) X ($!) = ($!, close(S)); # close S while saving $! X return undef; X } X unless (bind(S, $thisproc)) { X ($!) = ($!, close(S)); # close S while saving $! X return undef; X } X unless (connect(S, $serverproc)) { X ($!) = ($!, close(S)); # close S while saving $! X return undef; X } X select((select(S), $| = 1)[0]); X $next; # return symbol for switcharound X} X X## ($host, $port, $handle) = &chat'open_listen(); X## opens a TCP port on the current machine, ready to be listened to X Xsub open_listen { ## public X X *S = ++$next; X local(*NS) = "__" . time; X unless (socket(NS, 2, 1, 6)) { X # XXX hardwired $AF_SOCKET, $SOCK_STREAM, 'tcp' X # but who the heck would change these anyway? (:-) X ($!) = ($!, close(NS)); X return undef; X } X unless (bind(NS, $thisproc)) { X ($!) = ($!, close(NS)); X return undef; X } X unless (listen(NS, 1)) { X ($!) = ($!, close(NS)); X return undef; X } X select((select(NS), $| = 1)[0]); X local($family, $port, @myaddr) = X unpack("S n C C C C x8", getsockname(NS)); X $S{"needs_accept"} = *NS; # so expect will open it X (@myaddr, $port, $next); # returning this X} X X## $handle = &chat'open_proc("command","arg1","arg2",...); X## opens a /bin/sh on a pseudo-tty X Xsub open_proc { ## public X local(@cmd) = @_; X X *S = ++$next; X local(*TTY) = "__TTY" . time; X local($pty,$tty) = &_getpty(S,TTY); X die "Cannot find a new pty" unless defined $pty; X local($pid) = fork; X die "Cannot fork: $!" unless defined $pid; X unless ($pid) { X close STDIN; close STDOUT; close STDERR; X setpgrp(0,$$); X if (open(DEVTTY, "/dev/tty")) { X ioctl(DEVTTY,0x20007471,0); # XXX s/b &TIOCNOTTY X close DEVTTY; X } X open(STDIN,"<&TTY"); X open(STDOUT,">&TTY"); X open(STDERR,">&STDOUT"); X die "Oops" unless fileno(STDERR) == 2; # sanity X close(S); X exec @cmd; X die "Cannot exec @cmd: $!"; X } X close(TTY); X $next; # return symbol for switcharound X} X X# $S is the read-ahead buffer X X## $return = &chat'expect([$handle,] $timeout_time, X## $pat1, $body1, $pat2, $body2, ... ) X## $handle is from previous &chat'open_*(). X## $timeout_time is the time (either relative to the current time, or X## absolute, ala time(2)) at which a timeout event occurs. X## $pat1, $pat2, and so on are regexs which are matched against the input X## stream. If a match is found, the entire matched string is consumed, X## and the corresponding body eval string is evaled. X## X## Each pat is a regular-expression (probably enclosed in single-quotes X## in the invocation). ^ and $ will work, respecting the current value of $*. X## If pat is 'TIMEOUT', the body is executed if the timeout is exceeded. X## If pat is 'EOF', the body is executed if the process exits before X## the other patterns are seen. X## X## Pats are scanned in the order given, so later pats can contain X## general defaults that won't be examined unless the earlier pats X## have failed. X## X## The result of eval'ing body is returned as the result of X## the invocation. Recursive invocations are not thought X## through, and may work only accidentally. :-) X## X## undef is returned if either a timeout or an eof occurs and no X## corresponding body has been defined. X## I/O errors of any sort are treated as eof. X Xsub expect { ## public X if ($_[0] =~ /$nextpat/) { X *S = shift; X } X local($endtime) = shift; X X $endtime += time if $endtime < 600_000_000; X local($rmask, $nfound, $timeleft, $thisbuf); X local($timeout,$eof) = (1,1); X local($cases,$pattern,$action); X local($caller) = caller; X local($return,@return); X local($returnvar) = wantarray ? '@return' : '$return'; X X if (defined $S{"needs_accept"}) { # is it a listen socket? X local(*NS) = $S{"needs_accept"}; X delete $S{"needs_accept"}; X $S{"needs_close"} = *NS; X unless(accept(S,NS)) { X ($!) = ($!, close(S), close(NS)); X return undef; X } X select((select(S), $| = 1)[0]); X } X X ## strategy: create a giant block inside $cases X $cases .= <<'ESQ'; X LOOP: { XESQ X while (@_) { X ($pattern,$action) = splice(@_,0,2); X if ($pattern =~ /^eof$/i) { X $cases .= <<"EDQ"; X if (\$eof) { X $returnvar = do { package $caller; $action; }; X last LOOP; X } XEDQ X $eof = 0; X } elsif ($pattern =~ /^timeout$/i) { X $cases .= <<"EDQ"; X if (\$timeout) { X $returnvar = do { package $caller; $action; }; X last LOOP; X } XEDQ X $timeout = 0; X } else { X $pattern =~ s#/#\\/#g; X $cases .= <<"EDQ"; X if (\$S =~ /$pattern/) { X \$S = \$'; X $returnvar = do { package $caller; $action; }; X last LOOP; X } XEDQ X } X } X $cases .= <<"EDQ" if $eof; X if (\$eof) { X $returnvar = undef; X last LOOP; X } XEDQ X $cases .= <<"EDQ" if $timeout; X if (\$timeout) { X $returnvar = undef; X last LOOP; X } XEDQ X $eof = $timeout = 0; X $cases .= <<'ESQ'; X $rmask = ""; X vec($rmask,fileno(S),1) = 1; X ($nfound, $rmask) = X select($rmask, undef, undef, $endtime - time); X if ($nfound) { X "<nfound = $nfound>"; X $nread = sysread(S, $thisbuf, 1024); X if ($nread > 0) { X $S .= $thisbuf; X } else { X $eof++, redo LOOP; # any error is also eof X } X } else { X $timeout++, redo LOOP; # timeout X } X redo LOOP; X } XESQ X eval $cases; die "$cases:\n$@" if $@; X if (wantarray) { X return @return; X } else { X return $return; X } X} X X## &chat'print([$handle,] @data) X## $handle is from previous &chat'open(). X## like print $handle @data X Xsub print { ## public X if ($_[0] =~ /$nextpat/) { X *S = shift; X } X print S @_; X} X X## &chat'close([$handle,]) X## $handle is from previous &chat'open(). X## like close $handle X Xsub close { ## public X if ($_[0] =~ /$nextpat/) { X *S = shift; X } X close(S); X if (defined $S{"needs_close"}) { # is it a listen socket? X local(*NS) = $S{"needs_close"}; X delete $S{"needs_close"}; X close(NS); X } X} X X# ($pty,$tty) = $chat'_getpty(PTY,TTY): X# internal procedure to get the next available pty. X# opens pty on handle PTY, and matching tty on handle TTY. X# returns undef if can't find a pty. X Xsub _getpty { ## private X local($_PTY,$_TTY) = @_; X $_PTY =~ s/^([^']+)$/(caller)[$[]."'".$1/e; X $_TTY =~ s/^([^']+)$/(caller)[$[]."'".$1/e; X local($pty,$tty); X for $bank (112..127) { X next unless -e sprintf("/dev/pty%c0", $bank); X for $unit (48..57) { X $pty = sprintf("/dev/pty%c%c", $bank, $unit); X open($_PTY,"+>$pty") || next; X select((select($_PTY), $| = 1)[0]); X ($tty = $pty) =~ s/pty/tty/; X open($_TTY,"+>$tty") || next; X select((select($_TTY), $| = 1)[0]); X system "stty nl>$tty"; X return ($pty,$tty); X } X } X undef; X} X X1; END_OF_FILE if test 7365 -ne `wc -c <'chat2.pl'`; then echo shar: \"'chat2.pl'\" unpacked with wrong size! fi # end of 'chat2.pl' fi if test -f 'ftp-ls-R' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ftp-ls-R'\" else echo shar: Extracting \"'ftp-ls-R'\" \(2658 characters\) sed "s/^X//" >'ftp-ls-R' <<'END_OF_FILE' X#!/usr/bin/perl X Xpush(@INC, '/local/merlyn/lib/perl'); X Xrequire 'chat2.pl'; X X$| = $trace = 1, shift if $ARGV[0] =~ /^-d/; # debug mode X X$host = shift || "localhost"; X$topdir = shift || "."; X$user = "anonymous"; X$pass = "merlyn\@iwarp.intel.com"; X X($Control = &chat'open_port($host,21)) || die "open control: $!"; Xdie "expected 2, got $_" X unless ($_ = &clisten(10)) =~ /^2/; X&ctalk("user $user\n"); Xdie "expected 3, got $_" X unless ($_ = &clisten(10)) =~ /^3/; X&ctalk("pass $pass\n"); Xdie "expected 2, got $_" X unless ($_ = &clisten(10)) =~ /^2/; X## all set up for a conversation X X@list = ($topdir); Xwhile ($dir = shift list) { X next if $all{$dir}++; X print "=== $dir ===\n"; X for (&list($dir)) { X print "$_\n"; X push(@list, "$dir/$1") X if /^d.*\s(\S+)\s*$/; X } X} X X## shutdown X&ctalk("quit\n"); X&clisten(5); # for trace X&chat'close($Control); Xexit(0); X Xsub ctalk { X local($text) = @_; X print "{$text}" if $trace; X &chat'print($Control,$text); X} X Xsub clisten { X local($secs) = @_; X local($return,$tmp); X while (1) { X $tmp = &chat'expect($Control, $secs, '(.*)\r?\n', '"$1\n"'); X print $tmp if $trace; X $return .= $tmp; X return $return if !length($tmp) || $tmp =~ /^\d\d\d /; X } X} X Xsub dopen { X local($_); X X local(@ret) = &chat'open_listen(); X &ctalk("port " . X join(",", @ret[0,1,2,3], int($ret[4]/256), $ret[4]%256) . X "\n"); X die "expected 2, got $_" X unless ($_ = &clisten(5)) =~ /^2/; X $Data = $ret[5]; X} X Xsub dtalk { X local($text) = @_; X print "{D:$text}" if $trace; X &chat'print($Data,$text); X} X Xsub dlisten { X local($secs) = @_; X local($return,$tmp); X while (1) { X $tmp = &chat'expect($Data, $secs, X '(.|\n)+', '$&', X TIMEOUT, '""', X EOF, 'undef'); X if (defined $tmp) { X print "[D:$tmp]" if $trace; X $return .= $tmp; X return $return unless length $tmp; X # if timeout, return what you have X } else { # eof X return $return; X # maybe undef X } X } X} X Xsub dclose { X &chat'close($Data); X} X Xsub nlst { X local($dir) = @_; X local(@files); X local($_); X X &dopen(); X &ctalk("nlst -a $dir/.\n"); X die "expected 1, got $_" X unless ($_ = &clisten(10)) =~ /^1/; X $_ = &dlisten(30); X @files = sort grep(!/^\.\.?$/, split(/\r?\n/)) X unless /^ls: /; X die "expected 2, got $_" X unless ($_ = &clisten(10)) =~ /^2/; X &dclose(); X @files; X} X Xsub list { X local($dir) = @_; X local(@files); X local($_,$tmp); X X &dopen(); X &ctalk("list -A $dir/.\n"); X die "expected 1, got $_" X unless ($_ = &clisten(10)) =~ /^(.*\n)*1/; X while (1) { X $tmp = &dlisten(30); X last unless defined $tmp; X $_ .= $tmp; X } X @files = sort grep(/^\S[rwx\-]{8}/, split(/\r?\n/)); X die "expected 2, got $_" X unless ($_ = &clisten(10)) =~ /^2/; X &dclose(); X @files; X} END_OF_FILE if test 2658 -ne `wc -c <'ftp-ls-R'`; then echo shar: \"'ftp-ls-R'\" unpacked with wrong size! fi chmod +x 'ftp-ls-R' # end of 'ftp-ls-R' fi echo shar: End of shell archive. exit 0 print "Just another Perl hacker," -- /=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\ | on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III | | merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn | \=Cute Quote: "Intel: putting the 'backward' in 'backward compatible'..."====/
rbj@uunet.UU.NET (Root Boy Jim) (05/08/91)
merlyn@iwarp.intel.com (Randal L. Schwartz) writes: > >so an invocation of > > ftp-ls-R prep.ai.mit.edu pub/gnu > >gives you all the GNU stuff. I suppose I should have seen where this was going ... Why do you think "rget" isn't in the ftp client? The answer isn't technical, it's political. People, PLEASE BE CAREFUL about where and when you point this thing. If you point it at us, please do so during the daytime. Everyone wants their mail delivered at night, so we're busy then. 5:39pm /home/rbj [rbj@uunet 7] df /ftp Filesystem kbytes used avail capacity Mounted on /dev/ds2 1142364 923731 104396 90% /usr/spool/ftp Yes, that's right, almost a gigabyte. Some others even have more. -- [rbj@uunet 1] stty sane unknown mode: sane
emv@ox.com (Ed Vielmetti) (05/08/91)
In article <132095@uunet.UU.NET> rbj@uunet.UU.NET (Root Boy Jim) writes:
Why do you think "rget" isn't in the ftp client?
The answer isn't technical, it's political.
It's always a problem when political considerations work their way
into technical developments. Certainly there are risks involved in
commands which have the potential of transferring large amounts of
data without too much effort; links clog up, computers get busy,
paying customers get unhappy that their machines don't work so hot.
But don't interfere with my ability to work efficiently.
People, PLEASE BE CAREFUL about where and when you point this thing.
If you point it at us, please do so during the daytime.
I would think that uunet would welcome the deveopment of better ftp
client tools, especially those which would facilitate the development
of efficient and effective shadow archives. Since much of UUNET's
gigabyte worth of stuff is shadows of stuff which is originally
available on other sites, it would seem reasonable to build tools to
periodically tap the original sites on the shoulder and note whether
anything new has happened there in the past month. (or be tapped on
the shoulder by something which watches for new developments, like
comp.archives.) Good things to do intelligent recursive ftp's should
be welcomed.
--
Edward Vielmetti, MSEN Inc. moderator, comp.archives emv@msen.com
"often those with the power to appoint will be on one side of a
controversial issue and find it convenient to use their opponent's
momentary stridency as a pretext to squelch them"
allbery@NCoast.ORG (Brandon S. Allbery KB8JRR/AA) (05/09/91)
As quoted from <132095@uunet.UU.NET> by rbj@uunet.UU.NET (Root Boy Jim): +--------------- | merlyn@iwarp.intel.com (Randal L. Schwartz) writes: | > | >so an invocation of | > | > ftp-ls-R prep.ai.mit.edu pub/gnu | > | >gives you all the GNU stuff. | | I suppose I should have seen where this was going ... | | Why do you think "rget" isn't in the ftp client? | The answer isn't technical, it's political. +--------------- I had a feeling someone would say that. Read the code (and the rest of his message) --- it sends an ls -lR listing of all the GNU stuff, not the actual GNU archives. ++Brandon -- Me: Brandon S. Allbery Ham: KB8JRR/AA 10m,6m,2m,220,440,1.2 Internet: allbery@NCoast.ORG (restricted HF at present) Delphi: ALLBERY AMPR: kb8jrr.AmPR.ORG [44.70.4.88] uunet!usenet.ins.cwru.edu!ncoast!allbery KB8JRR @ WA8BXN.OH
rbj@uunet.uu.net (Root Boy Jim) (05/16/91)
allbery@ncoast.ORG (Brandon S. Allbery KB8JRR/AA) writes: >As quoted from <132095@uunet.UU.NET> by rbj@uunet.UU.NET (Root Boy Jim): >| >| Why do you think "rget" isn't in the ftp client? >| The answer isn't technical, it's political. >+--------------- > >I had a feeling someone would say that. Read the code (and the rest of his >message) --- it sends an ls -lR listing of all the GNU stuff, not the actual >GNU archives. Even so, consider the size of our ls-lR.Z: 376 -rw-r--r-- 1 rick 371745 May 15 03:39 /usr/spool/ftp/ls-lR.Z The file is probably a megabyte of so uncompressed. and also: [rbj@uunet 29] zcat ~ftp/ls-lR.Z |wc 39816 227715 1711169 We don't need people stat'ing 4000 files several times a day, or worse yet, at night. As I said, please be careful. -- [rbj@uunet 1] stty sane unknown mode: sane
emv@ox.com (Ed Vielmetti) (05/16/91)
In article <1991May15.235542.3938@uunet.uu.net> rbj@uunet.uu.net (Root Boy Jim) writes: >I had a feeling someone would say that. Read the code (and the rest of his >message) --- it sends an ls -lR listing of all the GNU stuff, not the actual >GNU archives. Even so, consider the size of our ls-lR.Z: 376 -rw-r--r-- 1 rick 371745 May 15 03:39 /usr/spool/ftp/ls-lR.Z What fraction of this is -rw-r--r-- 1 16 21 5624 Sep 19 1989 part02.Z -rw-r--r-- 1 16 21 5504 Sep 19 1989 part03.Z -rw-r--r-- 1 16 21 4531 Sep 19 1989 part04.Z -rw-r--r-- 1 16 21 5045 Sep 19 1989 part05.Z -rw-r--r-- 1 16 21 3046 Sep 19 1989 part06.Z nonsense with close to zero information content? You want us to ftp back a third of a megabyte full of that? (not to mention all of the old outdated versions of software that uunet has long after new versions are announced.) no way. esp. not over this person's 19.2kb line. and also: [rbj@uunet 29] zcat ~ftp/ls-lR.Z |wc 39816 227715 1711169 We don't need people stat'ing 4000 files several times a day, or worse yet, at night. As I said, please be careful. you have control over your ftp server, turn it off it you don't like it; reject recursive directory descents at the top level or two of your hierarchy. if it's that bad, it's within your power to solve it by administrative policy rather than jawboning us poor coders into not writing code. uunet is very different from the ordinary anonymous ftp system; many of the other 1000 or so sites have adequate CPU to handle a well-aimed request. if the net as a whole is beating up on your machines so bad, why don't you drop a few grand on a nice scsi disk and a cheap risc machine, call it "ftp.uu.net", and let the world know that it's out there. should extend the life of your existing gear, and since it'll be non-paying customers who are stat'ing files left and right they will have no right to complain if things are too slow. block non-paying customers from the revenue system. obperl: (not quite even) it's worth your while to pick up the log_archie script just posted to comp.sources.misc; nice way of keeping a tidy account of archie search sessions. (but it's in awk. so use a2pl first if you feel obligated) -- Edward Vielmetti, vice president for research, MSEN Inc. emv@msen.com "(6) The Plan shall identify how agencies and departments can collaborate to ... expand efforts to improve, document, and evaluate unclassified public-domain software developed by federally-funded researchers and other software, including federally-funded educational and training software; " "High-Performance Computing Act of 1991, S. 272"
rbj@uunet.uu.net (Root Boy Jim) (05/17/91)
In article <EMV.91May16030640@poe.aa.ox.com> emv@ox.com (Ed Vielmetti) writes: ?In <1991May15.235542.3938@uunet.uu.net> rbj@uunet.uu.net (Root Boy Jim) writes: ? ? Even so, consider the size of our ls-lR.Z: ? 376 -rw-r--r-- 1 rick 371745 May 15 03:39 /usr/spool/ftp/ls-lR.Z ? ?What fraction of this is ? ?-rw-r--r-- 1 16 21 5624 Sep 19 1989 part02.Z ?-rw-r--r-- 1 16 21 5504 Sep 19 1989 part03.Z ?-rw-r--r-- 1 16 21 4531 Sep 19 1989 part04.Z ?-rw-r--r-- 1 16 21 5045 Sep 19 1989 part05.Z ?-rw-r--r-- 1 16 21 3046 Sep 19 1989 part06.Z [rbj@uunet 38] zcat ~ftp/ls-lR.Z | grep 'part[0-9]*.Z$' | wc 5274 42192 284667 from the line below: [rbj@uunet 42] expr 284667 / 17111 = 16% ?nonsense with close to zero information content? You want us to ftp ?back a third of a megabyte full of that? (not to mention all of the ?old outdated versions of software that uunet has long after new ?versions are announced.) no way. esp. not over this person's 19.2kb ?line. I wonder how much could be saved by deleting old versions. And sometimes the old versions are better (but rarely). Perhaps we should include a ls-sR.Z as well. I'd rather have you retrieve this list as a text file than to generate it on the fly. ? and also: [rbj@uunet 29] zcat ~ftp/ls-lR.Z |wc ? 39816 227715 1711169 ? ? We don't need people stat'ing 4000 files several times a day, ? or worse yet, at night. As I said, please be careful. ? ?you have control over your ftp server, turn it off it you don't like it. When you snarf something from another machine via anonymous FTP you are a guest in their house. Please act politely. ?it; reject recursive directory descents at the top level or two of ?your hierarchy. A good point. However, some people feel that it isn't really worth writing code to do this, especially since that puts the power into anyone's hands, including novices and pinheads alike. ?if it's that bad, it's within your power to solve it ?by administrative policy rather than jawboning us poor coders into not ?writing code. Yes, it appears that such attempts are fruitless. ?uunet is very different from the ordinary anonymous ftp system; There are bigger sites than us, better organized. Reorganization is on our to do list. ?many of the other 1000 or so sites have adequate CPU to handle a ?well-aimed request. That's all I was asking. Please aim well. We aim to please. ?if the net as a whole is beating up on your ?machines so bad, why don't you drop a few grand on a nice scsi disk ?and a cheap risc machine, call it "ftp.uu.net", and let the world know ?that it's out there. ftp.uu.net is a cname for uunet.uu.net, and we do ask people to use it. We may actually make it a separate machine someday. I suppose the real reason is that ~uucp == ~ftp. I suppose we could mount it via NFS tho. In another letter you mentioned better ftp client/servers. Well it just so happens that Rick is in charge of BSD ftp and has made several enhancements. They include restarting in the middle of transfers, sending the modification time, only transferring newer versions, etc. The world would be a better place if more people ran it. -- [rbj@uunet 1] stty sane unknown mode: sane