home *** CD-ROM | disk | FTP | other *** search
/ CD Actual Thematic 7: Programming / CDAT7.iso / Share / Editores / Perl5 / perl / bin / lwp-download < prev    next >
Encoding:
Text File  |  1997-08-10  |  5.7 KB  |  220 lines

  1. #perl -w
  2.     eval 'exec perl -S $0 "$@"'
  3.     if 0;
  4.  
  5. # $Id: lwp-download.PL,v 1.2 1996/12/04 14:48:59 aas Exp $
  6.  
  7. =head1 NAME
  8.  
  9. lwp-download - fetch large files from the net
  10.  
  11. =head1 SYNOPSIS
  12.  
  13.  lwp-download <url> [<local file>]
  14.  
  15. =head1 DESCRIPTION
  16.  
  17. The I<lwp-download> program will down load the document specified by the URL
  18. given as the first command line argument to a local file.  The local
  19. filename used to save the document is guessed from the URL unless
  20. specified as the second command line argument.
  21.  
  22. The I<lwp-download> program is implemented using the I<libwww-perl>
  23. library.  It is better suited to down load big files than the
  24. I<lwp-request> program because it does not store the file in memory.
  25. Another benefit is that it will keep you updated about it's progress
  26. and that you don't have any options to worry about.
  27.  
  28. =head1 EXAMPLE
  29.  
  30. Fetch the newest and greatest perl version:
  31.  
  32.  $ lwp-download http://www.perl.com/CPAN/src/latest.tar.gz
  33.  Saving to 'latest.tar.gz'...
  34.  1.47 MB received in 22 seconds (68.7 KB/sec)
  35.  
  36. =head1 AUTHOR
  37.  
  38. Gisle Aas <gisle@aas.no>
  39.  
  40. =cut
  41.  
  42. use LWP::UserAgent;
  43. use LWP::MediaTypes;
  44. use URI::URL;
  45. use strict;
  46.  
  47. $0 =~ s,.*/,,;  # only basename left in progname
  48.  
  49. my $url = url(shift || usage());
  50. my $argfile = shift;
  51.  
  52. my $ua = new LWP::UserAgent;
  53.  
  54. $ua->agent("lwp-download/0.1 " . $ua->agent);
  55.  
  56. my $req = new HTTP::Request GET => $url;
  57.  
  58. my $file;      # name of file we download into
  59. my $length;    # total number of bytes to download
  60. my $flength;   # formatted length
  61. my $size = 0;  # number of bytes received
  62. my $start_t;   # start time of download
  63. my $last_dur;  # time of last callback
  64.  
  65. my $shown = 0; # has we called the show() function yet
  66.  
  67. $SIG{INT} = sub { die "Interrupted\n"; };
  68.  
  69. $| = 1;  # autoflush
  70.  
  71. my $res = $ua->request($req,
  72.   sub {
  73.       unless($file) {
  74.       my $res = $_[1];
  75.       unless ($argfile) {
  76.           # must find a suitable name to use.  First thing
  77.           # to do is to look for the "Content-Disposition"
  78.           # header defined by RFC1806.  This is also supported
  79.           # by Netscape
  80.           my $cd = $res->header("Content-Disposition");
  81.           if ($cd && $cd =~ /\bfilename\s*=\s*(\S+)/) {
  82.           $file = $1;
  83.           $file =~ s/;$//;
  84.           $file =~ s/^([\"\'])(.*)\1$/$2/;
  85.           }
  86.         
  87.           # if this fails we try to make something from the URL
  88.           unless ($file) {
  89.           my $req = $res->request;  # now always there
  90.           my $rurl = $req ? $req->url : $url;
  91.           
  92.           $file = ($rurl->path_components)[-1];
  93.           unless (length $file) {
  94.               $file = "index";
  95.               my $suffix = media_suffix($res->content_type);
  96.               $file .= ".$suffix" if $suffix;
  97.           } elsif ($rurl->scheme eq 'ftp' ||
  98.                $file =~ /\.tgz$/      ||
  99.                $file =~ /\.tar(\.(Z|gz))?$/
  100.               ) {
  101.               # leave the filename as it was
  102.           } else {
  103.               my $ct = guess_media_type($file);
  104.               unless ($ct eq $res->content_type) {
  105.               # need a better suffix for this type
  106.               my $suffix = media_suffix($res->content_type);
  107.               $file .= ".$suffix" if $suffix;
  108.               }
  109.           }
  110.           }
  111.  
  112.           # Check if the file is already present
  113.           if (-f $file && -t) {
  114.           print "Overwrite $file? [y] ";
  115.           my $ans = <STDIN>;
  116.           exit if !defined($ans) || !($ans =~ /^y?\n/);
  117.           } else {
  118.           print "Saving to '$file'...\n";
  119.           }
  120.       } else {
  121.           $file = $argfile;
  122.       }
  123.       open(FILE, ">$file") || die "Can't open $file: $!";
  124.       $length = $res->content_length;
  125.       $flength = fbytes($length) if defined $length;
  126.       $start_t = time;
  127.       $last_dur = 0;
  128.       }
  129.       $size += length($_[0]);
  130.       print FILE $_[0];
  131.       if (defined $length) {
  132.       my $dur  = time - $start_t;
  133.       if ($dur != $last_dur) {  # don't update too often
  134.           $last_dur = $dur;
  135.           my $perc = $size / $length;
  136.           my $speed;
  137.           $speed = fbytes($size/$dur) . "/sec" if $dur > 3;
  138.           my $secs_left = fduration($dur/$perc - $dur);
  139.           $perc = int($perc*100);
  140.           my $show = "$perc% of $flength";
  141.           $show .= " (at $speed, $secs_left remaining)" if $speed;
  142.           show($show);
  143.       }
  144.       } else {
  145.       show( fbytes($size) . " received");
  146.       }
  147.   }
  148. );
  149.  
  150. if ($res->is_success || $res->message =~ /^Interrupted/) {
  151.     show("");  # clear text
  152.     print "\r";
  153.     print fbytes($size);
  154.     print " of ", fbytes($length) if defined($length) && $length != $size;
  155.     print " received";
  156.     my $dur = time - $start_t;
  157.     if ($dur) {
  158.     my $speed = fbytes($size/$dur) . "/sec";
  159.     print " in ", fduration($dur), " ($speed)";
  160.     }
  161.     print "\n";
  162.     my $died = $res->header("X-Died");
  163.     if ($died || !$res->is_success) {
  164.     if (-t) {
  165.         print "Transfer aborted.  Delete $file? [n] ";
  166.         my $ans = <STDIN>;
  167.         unlink($file) if defined($ans) && $ans =~ /^y\n/;
  168.     } else {
  169.         print "Transfer aborted, $file kept\n";
  170.     }
  171.     }
  172. } else {
  173.     print "\n" if $shown;
  174.     print "$0: Can't download: ", $res->code, " ", $res->message, "\n";
  175.     exit 1;
  176. }
  177.  
  178.  
  179. sub fbytes
  180. {
  181.     my $n = int(shift);
  182.     if ($n >= 1024 * 1024) {
  183.     return sprintf "%.3g MB", $n / (1024.0 * 1024);
  184.     } elsif ($n >= 1024) {
  185.     return sprintf "%.3g KB", $n / 1024.0;
  186.     } else {
  187.     return "$n bytes";
  188.     }
  189. }
  190.  
  191. sub fduration
  192. {
  193.     use integer;
  194.     my $secs = int(shift);
  195.     my $hours = $secs / (60*60);
  196.     $secs -= $hours * 60*60;
  197.     my $mins = $secs / 60;
  198.     $secs %= 60;
  199.     if ($hours) {
  200.     return "$hours hours $mins minutes";
  201.     } elsif ($mins >= 2) {
  202.     return "$mins minutes";
  203.     } else {
  204.     $secs += $mins * 60;
  205.     return "$secs seconds";
  206.     }
  207. }
  208.  
  209. sub show
  210. {
  211.     my $mess = shift;
  212.     print "\r$mess", (" " x (75 - length $mess));
  213.     $shown++;
  214. }
  215.  
  216. sub usage
  217. {
  218.     die "Usage: $0 <url> [<lpath>]\n";
  219. }
  220.