home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / bin / lwp-download < prev    next >
Encoding:
Text File  |  2006-06-19  |  8.3 KB  |  336 lines

  1. #!/usr/bin/perl -w
  2.  
  3. eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
  4.     if 0; # not running under some shell
  5.  
  6. # $Id: lwp-download,v 2.15 2004/12/11 14:02:59 gisle Exp $
  7.  
  8. =head1 NAME
  9.  
  10. lwp-download - Fetch large files from the web
  11.  
  12. =head1 SYNOPSIS
  13.  
  14. B<lwp-download> [B<-a>] <I<url>> [<I<local path>>]
  15.  
  16. =head1 DESCRIPTION
  17.  
  18. The B<lwp-download> program will save the file at I<url> to a local
  19. file.
  20.  
  21. If I<local path> is not specified, then the current directory is
  22. assumed.
  23.  
  24. If I<local path> is a directory, then the basename of the file to save
  25. is picked up from the Content-Disposition header or the URL of the
  26. response.  If the file already exists, then B<lwp-download> will
  27. prompt before it overwrites and will fail if its standard input is not
  28. a terminal.  This form of invocation will also fail is no acceptable
  29. filename can be derived from the sources mentioned above.
  30.  
  31. If I<local path> is not a directory, then it is simply used as the
  32. path to save into.
  33.  
  34. The I<lwp-download> program is implemented using the I<libwww-perl>
  35. library.  It is better suited to down load big files than the
  36. I<lwp-request> program because it does not store the file in memory.
  37. Another benefit is that it will keep you updated about its progress
  38. and that you don't have much options to worry about.
  39.  
  40. Use the C<-a> option to save the file in text (ascii) mode.  Might
  41. make a difference on dosish systems.
  42.  
  43. =head1 EXAMPLE
  44.  
  45. Fetch the newest and greatest perl version:
  46.  
  47.  $ lwp-download http://www.perl.com/CPAN/src/latest.tar.gz
  48.  Saving to 'latest.tar.gz'...
  49.  11.4 MB received in 8 seconds (1.43 MB/sec)
  50.  
  51. =head1 AUTHOR
  52.  
  53. Gisle Aas <gisle@aas.no>
  54.  
  55. =cut
  56.  
  57. #' get emacs out of quote mode
  58.  
  59. use strict;
  60.  
  61. use LWP::UserAgent ();
  62. use LWP::MediaTypes qw(guess_media_type media_suffix);
  63. use URI ();
  64. use HTTP::Date ();
  65.  
  66. my $progname = $0;
  67. $progname =~ s,.*/,,;    # only basename left in progname
  68. $progname =~ s,.*\\,, if $^O eq "MSWin32";
  69. $progname =~ s/\.\w*$//; # strip extension if any
  70.  
  71. #parse option
  72. use Getopt::Std;
  73. my %opt;
  74. unless (getopts('a', \%opt)) {
  75.     usage();
  76. }
  77.  
  78. my $url = URI->new(shift || usage());
  79. my $argfile = shift;
  80. usage() if defined($argfile) && !length($argfile);
  81. my $version = q$Revision: 2.15 $;
  82. $version =~ s/[^\d.]//g;
  83.  
  84. my $ua = LWP::UserAgent->new(
  85.    agent => "lwp-download/$version ",
  86.    keep_alive => 1,
  87.    env_proxy => 1,
  88. );
  89.  
  90. my $file;      # name of file we download into
  91. my $length;    # total number of bytes to download
  92. my $flength;   # formatted length
  93. my $size = 0;  # number of bytes received
  94. my $start_t;   # start time of download
  95. my $last_dur;  # time of last callback
  96.  
  97. my $shown = 0; # have we called the show() function yet
  98.  
  99. $SIG{INT} = sub { die "Interrupted\n"; };
  100.  
  101. $| = 1;  # autoflush
  102.  
  103. my $res = $ua->request(HTTP::Request->new(GET => $url),
  104.   sub {
  105.       unless(defined $file) {
  106.       my $res = $_[1];
  107.  
  108.       my $directory;
  109.       if (defined $argfile && -d $argfile) {
  110.           ($directory, $argfile) = ($argfile, undef);
  111.       }
  112.  
  113.       unless (defined $argfile) {
  114.           # must find a suitable name to use.  First thing
  115.           # to do is to look for the "Content-Disposition"
  116.           # header defined by RFC1806.  This is also supported
  117.           # by Netscape
  118.           my $cd = $res->header("Content-Disposition");
  119.           if ($cd && $cd =~ /\bfilename\s*=\s*(\S+)/) {
  120.           $file = $1;
  121.           $file =~ s/;$//;
  122.           $file =~ s/^([\"\'])(.*)\1$/$2/;
  123.           $file =~ s,.*[\\/],,;  # basename
  124.           }
  125.  
  126.           # if this fails we try to make something from the URL
  127.           unless ($file) {
  128.           my $req = $res->request;  # now always there
  129.           my $rurl = $req ? $req->url : $url;
  130.  
  131.           $file = ($rurl->path_segments)[-1];
  132.           if (!defined($file) || !length($file)) {
  133.               $file = "index";
  134.               my $suffix = media_suffix($res->content_type);
  135.               $file .= ".$suffix" if $suffix;
  136.           }
  137.           elsif ($rurl->scheme eq 'ftp' ||
  138.                $file =~ /\.t[bg]z$/   ||
  139.                $file =~ /\.tar(\.(Z|gz|bz2?))?$/
  140.               ) {
  141.               # leave the filename as it was
  142.           }
  143.           else {
  144.               my $ct = guess_media_type($file);
  145.               unless ($ct eq $res->content_type) {
  146.               # need a better suffix for this type
  147.               my $suffix = media_suffix($res->content_type);
  148.               $file .= ".$suffix" if $suffix;
  149.               }
  150.           }
  151.           }
  152.  
  153.           # validate that we don't have a harmful filename now.  The server
  154.           # might try to trick us into doing something bad.
  155.           if (!length($file) ||
  156.                   $file =~ s/([^a-zA-Z0-9\.\-\+\~])/sprintf "\\x%02x", ord($1)/ge)
  157.               {
  158.           die "Will not save <$url> as \"$file\".\nPlease override file name on the command line.\n";
  159.           }
  160.  
  161.           if (defined $directory) {
  162.               require File::Spec;
  163.               $file = File::Spec->catfile($directory, $file);
  164.           }
  165.  
  166.           # Check if the file is already present
  167.           if (-l $file) {
  168.           die "Will not save <$url> to link \"$file\".\nPlease override file name on the command line.\n";
  169.           }
  170.           elsif (-f _) {
  171.           die "Will not save <$url> as \"$file\" without verification.\nEither run from terminal or override file name on the command line.\n"
  172.               unless -t;
  173.           $shown = 1;
  174.           print "Overwrite $file? [y] ";
  175.           my $ans = <STDIN>;
  176.           unless (defined($ans) && $ans =~ /^y?\n/) {
  177.               if (defined $ans) {
  178.               print "Ok, aborting.\n";
  179.               }
  180.               else {
  181.               print "\nAborting.\n";
  182.               }
  183.               exit 1;
  184.           }
  185.           $shown = 0;
  186.           }
  187.           elsif (-e _) {
  188.           die "Will not save <$url> as \"$file\".  Path exists.\n";
  189.           }
  190.           else {
  191.           print "Saving to '$file'...\n";
  192.           }
  193.       }
  194.       else {
  195.           $file = $argfile;
  196.       }
  197.       open(FILE, ">$file") || die "Can't open $file: $!\n";
  198.           binmode FILE unless $opt{a};
  199.       $length = $res->content_length;
  200.       $flength = fbytes($length) if defined $length;
  201.       $start_t = time;
  202.       $last_dur = 0;
  203.       }
  204.  
  205.       print FILE $_[0] or die "Can't write to $file: $!\n";
  206.       $size += length($_[0]);
  207.  
  208.       if (defined $length) {
  209.       my $dur  = time - $start_t;
  210.       if ($dur != $last_dur) {  # don't update too often
  211.           $last_dur = $dur;
  212.           my $perc = $size / $length;
  213.           my $speed;
  214.           $speed = fbytes($size/$dur) . "/sec" if $dur > 3;
  215.           my $secs_left = fduration($dur/$perc - $dur);
  216.           $perc = int($perc*100);
  217.           my $show = "$perc% of $flength";
  218.           $show .= " (at $speed, $secs_left remaining)" if $speed;
  219.           show($show, 1);
  220.       }
  221.       }
  222.       else {
  223.       show( fbytes($size) . " received");
  224.       }
  225.   }
  226. );
  227.  
  228. if (fileno(FILE)) {
  229.     close(FILE) || die "Can't write to $file: $!\n";
  230.  
  231.     show("");  # clear text
  232.     print "\r";
  233.     print fbytes($size);
  234.     print " of ", fbytes($length) if defined($length) && $length != $size;
  235.     print " received";
  236.     my $dur = time - $start_t;
  237.     if ($dur) {
  238.     my $speed = fbytes($size/$dur) . "/sec";
  239.     print " in ", fduration($dur), " ($speed)";
  240.     }
  241.     print "\n";
  242.  
  243.     if (my $mtime = $res->last_modified) {
  244.     utime time, $mtime, $file;
  245.     }
  246.  
  247.     if ($res->header("X-Died") || !$res->is_success) {
  248.     if (my $died = $res->header("X-Died")) {
  249.         print "$died\n";
  250.     }
  251.     if (-t) {
  252.         print "Transfer aborted.  Delete $file? [n] ";
  253.         my $ans = <STDIN>;
  254.         if (defined($ans) && $ans =~ /^y\n/) {
  255.         unlink($file) && print "Deleted.\n";
  256.         }
  257.         elsif ($length > $size) {
  258.         print "Truncated file kept: ", fbytes($length - $size), " missing\n";
  259.         }
  260.         else {
  261.         print "File kept.\n";
  262.         }
  263.             exit 1;
  264.     }
  265.     else {
  266.         print "Transfer aborted, $file kept\n";
  267.     }
  268.     }
  269.     exit 0;
  270. }
  271.  
  272. # Did not manage to create any file
  273. print "\n" if $shown;
  274. if (my $xdied = $res->header("X-Died")) {
  275.     print "$progname: Aborted\n$xdied\n";
  276. }
  277. else {
  278.     print "$progname: ", $res->status_line, "\n";
  279. }
  280. exit 1;
  281.  
  282.  
  283. sub fbytes
  284. {
  285.     my $n = int(shift);
  286.     if ($n >= 1024 * 1024) {
  287.     return sprintf "%.3g MB", $n / (1024.0 * 1024);
  288.     }
  289.     elsif ($n >= 1024) {
  290.     return sprintf "%.3g KB", $n / 1024.0;
  291.     }
  292.     else {
  293.     return "$n bytes";
  294.     }
  295. }
  296.  
  297. sub fduration
  298. {
  299.     use integer;
  300.     my $secs = int(shift);
  301.     my $hours = $secs / (60*60);
  302.     $secs -= $hours * 60*60;
  303.     my $mins = $secs / 60;
  304.     $secs %= 60;
  305.     if ($hours) {
  306.     return "$hours hours $mins minutes";
  307.     }
  308.     elsif ($mins >= 2) {
  309.     return "$mins minutes";
  310.     }
  311.     else {
  312.     $secs += $mins * 60;
  313.     return "$secs seconds";
  314.     }
  315. }
  316.  
  317.  
  318. BEGIN {
  319.     my @ani = qw(- \ | /);
  320.     my $ani = 0;
  321.  
  322.     sub show
  323.     {
  324.         my($mess, $show_ani) = @_;
  325.         print "\r$mess" . (" " x (75 - length $mess));
  326.     print $show_ani ? "$ani[$ani++]\b" : " ";
  327.         $ani %= @ani;
  328.         $shown++;
  329.     }
  330. }
  331.  
  332. sub usage
  333. {
  334.     die "Usage: $progname [-a] <url> [<lpath>]\n";
  335. }
  336.