2 ## Jeffrey Friedl (jfriedl@omron.co.jp)
3 ## Copyri.... ah hell, just take it.
6 ## Include (require) to use, execute ("perl www.pl") to print a man page.
7 ## Requires my 'network.pl' library.
13 ## -- oops, stopped sending garbage Authorization line when no
14 ## authorization was requested.
17 ## -- added support for HEAD, If-Modified-Since
20 ## -- Change to allow a POST'ed HTTP text to have newlines in it.
21 ## Added 'NewURL to the open_http_connection %info. Idea courtesy
22 ## of Bryan Schmersal (http://www.transarc.com/~bryans/Home.html).
26 ## -- added more robust HTTP error reporting
27 ## (due to steven_campbell@uk.ibm.com)
30 ## -- added Authorization support
34 ## HTTP return status codes.
40 203,"Partial Information",
48 402,"Payment required",
52 501,"Not implemented",
53 502,"Service temporarily overloaded",
54 503,"Gateway timeout");
57 ## If executed directly as a program, print as a man page.
59 if (length($0) >= 6 && substr($0, -6) eq 'www.pl')
61 seek(DATA, 0, 0) || die "$0: can't reset internal pointer.\n";
62 print "www.pl version $version\n", '=' x 60, "\n";
64 next unless /^##>/../^##</; ## select lines to print
65 s/^##[<> ]?//; ## clean up
74 ## added require for "network.pl"
77 ## re-did from "Www.pl" which was a POS.
81 ## A group of routines for dealing with URLs, HTTP sessions, proxies, etc.
82 ## Requires my 'network.pl' package. The library file can be executed
83 ## directly to produce a man page.
86 ## A motley group of routines for dealing with URLs, HTTP sessions, proxies,
87 ## etc. Requires my 'network.pl' package.
89 ## Latest version, as well as other stuff (including network.pl) available
90 ## at http://www.wg.omron.co.jp/~jfriedl/perl/
92 ## Simpleton complete program to dump a URL given on the command-line:
94 ## require 'network.pl'; ## required for www.pl
95 ## require 'www.pl'; ## main routines
96 ## $URL = shift; ## get URL
97 ## ($status, $memo) = &www'open_http_url(*IN, $URL); ## connect
98 ## die "$memo\n" if $status ne 'ok'; ## report any error
99 ## print while <IN>; ## dump contents
101 ## There are various options available for open_http_url.
102 ## For example, adding 'quiet' to the call, i.e. vvvvvvv-----added
103 ## ($status, $memo) = &www'open_http_url(*IN, $URL, 'quiet');
104 ## suppresses the normal informational messages such as "waiting for data...".
106 ## The options, as well as the various other public routines in the package,
107 ## are discussed below.
112 ## Default port for the protocols whose URL we'll at least try to recognize.
114 %default_port = ('http', 80,
122 ## A "URL" to "ftp.blah.com" without a protocol specified is probably
123 ## best reached via ftp. If the hostname begins with a protocol name, it's
124 ## easy. But something like "www." maps to "http", so that mapping is below:
131 $last_message_length = 0;
132 $useragent = "www.pl/$version";
136 ##############################################################################
137 ## routine: open_http_url
140 ## ($status, $memo, %info) = &www'open_http_url(*FILEHANDLE, $URL, options..)
142 ## Given an unused filehandle, a URL, and a list of options, opens a socket
143 ## to the URL and returns with the filehandle ready to read the data of the
144 ## URL. The HTTP header, as well as other information, is returned in %info.
146 ## OPTIONS are from among:
149 ## If PATH appears to be a query (i.e. has a ? in it), contact
150 ## via a POST rather than a GET.
153 ## Normally, if the initial contact indicates that the URL has moved
154 ## to a different location, the new location is automatically contacted.
155 ## "nofollow" inhibits this.
158 ## Normally, a proxy will be used if 'http_proxy' is defined in the
159 ## environment. This option inhibits the use of a proxy.
162 ## If a host's address can't be found, it may well be because the
163 ## nslookup just didn't return in time and that retrying the lookup
164 ## after a few seconds will succeed. If this option is given, will
165 ## wait five seconds and try again. May be given multiple times to
166 ## retry multiple times.
169 ## Informational messages will be suppressed.
172 ## Additional messages will be printed.
175 ## Requests only the file header to be sent
180 ## The return array is ($STATUS, $MEMO, %INFO).
182 ## STATUS is 'ok', 'error', 'status', or 'follow'
184 ## If 'error', the MEMO will indicate why (URL was not http, can't
185 ## connect, etc.). INFO is probably empty, but may have some data.
188 ## If 'status', the connnection was made but the reply was not a normal
189 ## "OK" successful reply (i.e. "Not found", etc.). MEMO is a note.
190 ## INFO is filled as noted below. Filehandle is ready to read (unless
191 ## $info{'BODY'} is filled -- see below), but probably most useful
192 ## to treat this as an 'error' response.
194 ## If 'follow', MEMO is the new URL (for when 'nofollow' was used to
195 ## turn off automatic following) and INFO is filled as described
196 ## below. Unless you wish to give special treatment to these types of
197 ## responses, you can just treat 'follow' responses like 'ok'
200 ## If 'ok', the connection went well and the filehandle is ready to
203 ## INFO contains data as described at the read_http_header() function (in
204 ## short, the HTTP response header) and additional informational fields.
205 ## In addition, the following fields are filled in which describe the raw
206 ## connection made or attempted:
208 ## PROTOCOL, HOST, PORT, PATH
210 ## Note that if a proxy is being used, these will describe the proxy.
211 ## The field TARGET will describe the host or host:port ultimately being
212 ## contacted. When no proxy is being used, this will be the same info as
213 ## in the raw connection fields above. However, if a proxy is being used,
214 ## it will refer to the final target.
216 ## In some cases, the additional entry $info{'BODY'} exists as well. If
217 ## the result-code indicates an error, the body of the message may be
218 ## parsed for internal reasons (i.e. to support 'repeat'), and if so, it
219 ## will be saved in $info{'BODY}.
221 ## If the URL has moved, $info{'NewURL'} will exist and contain the new
222 ## URL. This will be true even if the 'nofollow' option is specified.
228 local(*HTTP, $URL, @options) = @_;
229 return &open_http_connection(*HTTP, $URL, undef, undef, undef, @options);
235 ##############################################################################
236 ## routine: read_http_header
238 ## Given a filehandle to a just-opened HTTP socket connection (such as one
239 ## created via &network'connect_to which has had the HTTP request sent),
240 ## reads the HTTP header and and returns the parsed info.
242 ## ($replycode, %info) = &read_http_header(*FILEHANDLE);
244 ## $replycode will be the HTTP reply code as described below, or
245 ## zero on header-read error.
247 ## %info contains two types of fields:
249 ## Upper-case fields are informational from the function.
250 ## Lower-case fields are the header field/value pairs.
252 ## Upper-case fields:
254 ## $info{'STATUS'} will be the first line read (HTTP status line)
256 ## $info{'CODE'} will be the numeric HTTP reply code from that line.
257 ## This is also returned as $replycode.
259 ## $info{'TYPE'} is the text from the status line that follows CODE.
261 ## $info{'HEADER'} will be the raw text of the header (sans status line),
264 ## $info{'UNKNOWN'}, if defined, will be any header lines not in the
265 ## field/value format used to fill the lower-case fields of %info.
267 ## Lower-case fields are reply-dependent, but in general are described
268 ## in http://info.cern.ch/hypertext/WWW/Protocols/HTTP/Object_Headers.html
270 ## A header line such as
271 ## Content-type: Text/Plain
272 ## will appear as $info{'content-type'} = 'Text/Plain';
274 ## (*) Note that while the field names are are lower-cased, the field
275 ## values are left as-is.
278 ## When $replycode is zero, there are two possibilities:
279 ## $info{'TYPE'} is 'empty'
280 ## No response was received from the filehandle before it was closed.
281 ## No other %info fields present.
282 ## $info{'TYPE'} is 'unknown'
283 ## First line of the response doesn't seem to be proper HTTP.
284 ## $info{'STATUS'} holds that line. No other %info fields present.
286 ## The $replycode, when not zero, is as described at
287 ## http://info.cern.ch/hypertext/WWW/Protocols/HTTP/HTRESP.html
289 ## Some of the codes:
295 ## partial information 203
305 ## paymentrequired 402
308 ## internal error 500
309 ## not implemented 501
310 ## service temporarily overloaded 502
311 ## gateway timeout 503
321 ## The first line of the response will be the status (OK, error, etc.)
323 unless (defined($info{'STATUS'} = <HTTP>)) {
324 $info{'TYPE'} = "empty";
327 chop $info{'STATUS'};
330 ## Check the status line. If it doesn't match and we don't know the
331 ## format, we'll just let it pass and hope for the best.
333 unless ($info{'STATUS'} =~ m/^HTTP\S+\s+(\d\d\d)\s+(.*\S)/i) {
334 $info{'TYPE'} = 'unknown';
340 $info{'HEADER'} = '';
342 ## read the rest of the header.
345 $info{'HEADER'} .= $_; ## save whole text of header.
347 if (m/^([^\n:]+):[ \t]*(.*\S)/) {
348 local($field, $value) = ("\L$1", $2);
349 if (defined $info{$field}) {
350 $info{$field} .= "\n" . $value;
352 $info{$field} = $value;
354 } elsif (defined $info{'UNKNOWN'}) {
355 $info{'UNKNOWN'} .= $_;
357 $info{'UNKNOWN'} = $_;
361 return ($info{'CODE'}, %info);
367 ##############################################################################
368 ## routine: grok_URL(URL, noproxy, defaultprotocol)
370 ## Given a URL, returns access information. Deals with
371 ## http, wais, gopher, ftp, and telnet
374 ## Information returned is
375 ## (PROTOCOL, HOST, PORT, PATH, TARGET, USER, PASSWORD)
377 ## If noproxy is not given (or false) and there is a proxy defined
378 ## for the given protocol (via the "*_proxy" environmental variable),
379 ## the returned access information will be for the proxy and will
380 ## reference the given URL. In this case, 'TARGET' will be the
381 ## HOST:PORT of the original URL (PORT elided if it's the default port).
383 ## Access information returned:
384 ## PROTOCOL: "http", "ftp", etc. (guaranteed to be lowercase).
385 ## HOST: hostname or address as given.
386 ## PORT: port to access
387 ## PATH: path of resource on HOST:PORT.
388 ## TARGET: (see above)
389 ## USER and PASSWORD: for 'ftp' and 'telnet' URLs, if supplied by the
390 ## URL these will be defined, undefined otherwise.
392 ## If no protocol is defined via the URL, the defaultprotocol will be used
393 ## if given. Otherwise, the URL's address will be checked for a leading
394 ## protocol name (as with a leading "www.") and if found will be used.
395 ## Otherwise, the protocol defaults to http.
397 ## Fills in the appropriate default port for the protocol if need be.
399 ## A proxy is defined by a per-protocol environmental variable such
400 ## as http_proxy. For example, you might have
401 ## setenv http_proxy http://firewall:8080/
402 ## setenv ftp_proxy $http_proxy
405 ## A URL seems to be officially described at
406 ## http://www.w3.org/hypertext/WWW/Addressing/URL/5_BNF.html
407 ## although that document is a joke of errors.
413 local($_, $noproxy, $defaultprotocol) = @_;
414 $noproxy = defined($noproxy) && $noproxy;
416 ## Items to be filled in and returned.
417 local($protocol, $address, $port, $path, $target, $user, $password);
419 return undef unless m%^(([a-zA-Z]+)://|/*)([^/]+)(/.*)?$%;
422 ## Due to a bug in some versions of perl5, $2 might not be empty
423 ## even if $1 is. Therefore, we must check $1 for a : to see if the
424 ## protocol stuff matched or not. If not, the protocol is undefined.
426 ($protocol, $address, $path) = ((index($1,":") >= 0 ? $2 : undef), $3, $4);
428 if (!defined $protocol)
431 ## Choose a default protocol if none given. If address begins with
432 ## a protocol name (one that we know via %name2protocol or
433 ## %default_port), choose it. Otherwise, choose http.
435 if (defined $defaultprotocol) {
436 $protocol = $defaultprotocol;
440 $address =~ m/^[a-zA-Z]+/;
441 if (defined($name2protocol{"\L$&"})) {
442 $protocol = $name2protocol{"\L$&"};
444 $protocol = defined($default_port{"\L$&"}) ? $& : 'http';
448 $protocol =~ tr/A-Z/a-z/; ## ensure lower-case.
451 ## Http support here probably not kosher, but fits in nice for basic
454 if ($protocol eq 'ftp' || $protocol eq 'telnet' || $protocol eq 'http')
456 ## Glean a username and password from address, if there.
457 ## There if address starts with USER[:PASSWORD]@
458 if ($address =~ s/^(([^\@:]+)(:([^@]+))?\@)//) {
459 ($user, $password) = ($2, $4);
464 ## address left is (HOSTNAME|HOSTNUM)[:PORTNUM]
466 if ($address =~ s/:(\d+)$//) {
469 $port = $default_port{$protocol};
472 ## default path is '/';
473 $path = '/' if !defined $path;
476 ## If there's a proxy and we're to proxy this request, do so.
478 local($proxy) = $ENV{$protocol."_proxy"};
479 if (!$noproxy && defined($proxy) && !&no_proxy($protocol,$address))
482 local($old_pass, $old_user);
485 ## Since we're going through a proxy, we want to send the
486 ## proxy the entire URL that we want. However, when we're
487 ## doing Authenticated HTTP, we need to take out the user:password
488 ## that webget has encoded in the URL (this is a bit sleazy on
489 ## the part of webget, but the alternative is to have flags, and
490 ## having them part of the URL like with FTP, etc., seems a bit
491 ## cleaner to me in the context of how webget is used).
493 ## So, if we're doing this slezy thing, we need to construct
494 ## the new URL from the compnents we have now (leaving out password
495 ## and user), decode the proxy URL, then return the info for
496 ## that host, a "filename" of the entire URL we really want, and
497 ## the user/password from the original URL.
499 ## For all other things, we can just take the original URL,
500 ## ensure it has a protocol on it, and pass it as the "filename"
501 ## we want to the proxy host. The difference between reconstructing
502 ## the URL (as for HTTP Authentication) and just ensuring the
503 ## protocol is there is, except for the user/password stuff,
504 ## nothing. In theory, at least.
506 if ($protocol eq 'http' && (defined($password) || defined($user)))
508 $path = "http://$address$path";
509 $old_pass = $password;
512 ## Re-get original URL and ensure protocol// actually there.
513 ## This will become our new path.
514 ($path = $_) =~ s,^($protocol:)?/*,$protocol://,i;
517 ## note what the target will be
518 $target = ($port==$default_port{$protocol})?$address:"$address:$port";
520 ## get proxy info, discarding
521 ($protocol, $address, $port, $dummy, $dummy, $user, $password)
522 = &grok_URL($proxy, 1);
523 $password = $old_pass if defined $old_pass;
524 $user = $old_user if defined $old_user;
526 ($protocol, $address, $port, $path, $target, $user, $password);
532 ## &no_proxy($protocol, $host)
534 ## Returns true if the specified host is identified in the no_proxy
535 ## environmental variable, or identify the proxy server itself.
539 local($protocol, $targethost) = @_;
540 local(@dests, $dest, $host, @hosts, $aliases);
541 local($proxy) = $ENV{$protocol."_proxy"};
542 return 0 if !defined $proxy;
543 $targethost =~ tr/A-Z/a-z/; ## ensure all lowercase;
546 push(@dests,split(/\s*,\s*/,$ENV{'no_proxy'})) if defined $ENV{'no_proxy'};
548 foreach $dest (@dests)
550 ## just get the hostname
551 $host = (&grok_URL($dest, 1), 'http')[1];
553 if (!defined $host) {
554 warn "can't grok [$dest] from no_proxy env.var.\n";
557 @hosts = ($host); ## throw in original name just to make sure
558 ($host, $aliases) = (gethostbyname($host))[0, 1];
560 if (defined $aliases) {
561 push(@hosts, ($host, split(/\s+/, $aliases)));
565 foreach $host (@hosts) {
566 next if !defined $host;
567 return 1 if "\L$host" eq $targethost;
573 sub ensure_proper_network_library
575 require 'network.pl' if !defined $network'version;
576 warn "WARNING:\n". __FILE__ .
577 qq/ needs a newer version of "network.pl"\n/ if
578 !defined($network'version) || $network'version < "950311.5";
585 ##############################################################################
586 ## open_http_connection(*FILEHANDLE, HOST, PORT, PATH, TARGET, OPTIONS...)
588 ## Opens an HTTP connection to HOST:PORT and requests PATH.
589 ## TARGET is used only for informational messages to the user.
591 ## If PORT and PATH are undefined, HOST is taken as an http URL and TARGET
592 ## is filled in as needed.
594 ## Otherwise, it's the same as open_http_url (including return value, etc.).
597 sub open_http_connection
599 local(*HTTP, $host, $port, $path, $target, @options) = @_;
600 local($post_text, @error, %seen);
603 &ensure_proper_network_library;
606 local($post, $retry, $authorization, $nofollow, $noproxy,
607 $head, $debug, $ifmodifiedsince, $quiet, ) = (0) x 10;
609 foreach $opt (@options)
611 next unless defined($opt) && $opt ne '';
613 if ($opt =~ m/^(\w+)=(.*)/) {
614 ($var, $val) = ($1, $2);
619 $var =~ tr/A-Z/a-z/; ## ensure variable is lowercase.
622 eval "if (defined \$$var) { \$$var = \$val; } else { \@error =
623 ('error', 'bad open_http_connection option [$opt]'); }";
624 return ('error', "open_http_connection eval: $@") if $@;
625 return @error if defined @error;
627 $quiet = 0 if $debug; ## debug overrides quiet
629 local($protocol, $error, $code, $URL, %info, $tmp, $aite);
632 ## if both PORT and PATH are undefined, treat HOST as a URL.
634 unless (defined($port) && defined($path))
636 ($protocol,$host,$port,$path,$target)=&grok_URL($host,$noproxy,'http');
637 if ($protocol ne "http") {
638 return ('error',"open_http_connection doesn't grok [$protocol]");
640 unless (defined($host)) {
641 return ('error', "can't grok [$URL]");
645 return ('error', "no port in URL [$URL]") unless defined $port;
646 return ('error', "no path in URL [$URL]") unless defined $path;
650 ## we'll want $URL around for error messages and such.
651 if ($port == $default_port{'http'}) {
652 $URL = "http://$host";
654 $URL = "http://$host:$default_port{'http'}";
656 $URL .= ord($path) eq ord('/') ? $path : "/$path";
658 $aite = defined($target) ? "$target via $host" : $host;
660 &message($debug, "connecting to $aite ...") unless $quiet;
663 ## note some info that might be of use to the caller.
671 if (defined $target) {
672 $preinfo{'TARGET'} = $target;
673 } elsif ($default_port{'http'} == $port) {
674 $preinfo{'TARGET'} = $host;
676 $preinfo{'TARGET'} = "$host:$port";
679 ## connect to the site
680 $error = &network'connect_to(*HTTP, $host, $port);
681 if (defined $error) {
682 return('error', "can't connect to $aite: $error", %preinfo);
685 ## If we're asked to POST and it looks like a POST, note post text.
686 if ($post && $path =~ m/\?/) {
687 $post_text = $'; ## everything after the '?'
688 $path = $`; ## everything before the '?'
691 ## send the POST or GET request
692 $tmp = $head ? 'HEAD' : (defined $post_text ? 'POST' : 'GET');
694 &message($debug, "sending request to $aite ...") if !$quiet;
695 print HTTP $tmp, " $path HTTP/1.0\n";
697 ## send the If-Modified-Since field if needed.
698 if ($ifmodifiedsince) {
699 print HTTP "If-Modified-Since: $ifmodifiedsince\n";
702 ## oh, let's sputter a few platitudes.....
703 print HTTP "Accept: */*\n";
704 print HTTP "User-Agent: $useragent\n" if defined $useragent;
706 ## If doing Authorization, do so now.
707 if ($authorization) {
708 print HTTP "Authorization: Basic ",
709 &htuu_encode($authorization), "\n";
712 ## If it's a post, send it.
713 if (defined $post_text)
715 print HTTP "Content-type: application/x-www-form-urlencoded\n";
716 print HTTP "Content-length: ", length $post_text, "\n\n";
717 print HTTP $post_text, "\n";
720 &message($debug, "waiting for data from $aite ...") unless $quiet;
722 ## we can now read the response (header, then body) via HTTP.
723 binmode(HTTP); ## just in case.
725 ($code, %info) = &read_http_header(*HTTP);
726 &message(1, "header returns code $code ($info{'TYPE'})") if $debug;
728 ## fill in info from %preinfo
730 while (($val, $key) = each %preinfo) {
736 return('error',"empty response for $URL")
737 if $info{'TYPE'} eq 'empty';
738 return('error', "non-HTTP response for $URL", %info)
739 if $info{'TYPE'} eq 'unknown';
740 return('error', "unknown zero-code for $URL", %info);
743 if ($code == 302) ## 302 is magic for "Found"
745 if (!defined $info{'location'}) {
746 return('error', "No location info for Found URL $URL", %info);
748 local($newURL) = $info{'location'};
750 ## Remove :80 from hostname, if there. Looks ugly.
751 $newURL =~ s,^(http:/+[^/:]+):80/,$1/,i;
752 $info{"NewURL"} = $newURL;
754 ## if we're not following links or if it's not to HTTP, return.
755 return('follow', $newURL, %info) if
756 $nofollow || $newURL!~m/^http:/i;
758 ## note that we've seen this current URL.
759 $seen{$host, $port, $path} = 1;
761 &message(1, qq/[note: now moved to "$newURL"]/) unless $quiet;
764 ## get the new one and return an error if it's been seen.
765 ($protocol, $host, $port, $path, $target) =
766 &www'grok_URL($newURL, $noproxy);
767 &message(1, "[$protocol][$host][$port][$path]") if $debug;
769 if (defined $seen{$host, $port, $path})
771 return('error', "circular reference among:\n ".
772 join("\n ", sort grep(/^http/i, keys %seen)), %seen);
776 elsif ($code == 500) ## 500 is magic for "internal error"
779 ## A proxy will often return this with text saying "can't find
780 ## host" when in reality it's just because the nslookup returned
781 ## null at the time. Such a thing should be retied again after a
786 local($_) = $info{'BODY'} = join('', <HTTP>);
787 if (/Can't locate remote host:\s*(\S+)/i) {
788 local($times) = ($retry == 1) ?
789 "once more" : "up to $retry more times";
790 &message(0, "can't locate $1, will try $times ...")
799 if ($code != 200) ## 200 is magic for "OK";
801 ## I'll deal with these as I see them.....
803 if ($info{'TYPE'} eq '')
805 if (defined $http_return_code{$code}) {
806 $info{'TYPE'} = $http_return_code{$code};
808 $info{'TYPE'} = "(unknown status code $code)";
811 return ('status', $info{'TYPE'}, %info);
815 return ('ok', 'ok', %info);
821 ## Hyper Text UUencode. Somewhat different from regular uuencode.
823 ## Logic taken from Mosaic for X code by Mark Riordan and Ari Luotonen.
827 local(@in) = unpack("C*", $_[0]);
830 push(@in, 0, 0); ## in case we need to round off an odd byte or two
833 ## From the next three input bytes,
834 ## construct four encoded output bytes.
836 push(@out, $in[0] >> 2);
837 push(@out, (($in[0] << 4) & 060) | (($in[1] >> 4) & 017));
838 push(@out, (($in[1] << 2) & 074) | (($in[2] >> 6) & 003));
839 push(@out, $in[2] & 077);
840 splice(@in, 0, 3); ## remove these three
844 ## @out elements are now indices to the string below. Convert to
845 ## the appropriate actual text.
847 foreach $new (@out) {
849 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
854 ## the two left over are the two extra nulls, so we encoded the proper
857 ## We encoded one extra null too many. Undo it.
860 ## We must have encoded two nulls... Undo both.
862 $out[$#out -1] = '=';
869 ## This message stuff really shouldn't be here, but in some seperate library.
872 ## Called as &message(SAVE, TEXT ....), it shoves the text to the screen.
873 ## If SAVE is true, bumps the text out as a printed line. Otherwise,
874 ## will shove out without a newline so that the next message overwrites it,
875 ## or it is clearded via &clear_message().
880 die "oops $nl." unless $nl =~ m/^\d+$/;
881 local($text) = join('', @_);
882 local($NL) = $nl ? "\n" : "\r";
883 $thislength = length($text);
884 if ($thislength >= $last_message_length) {
885 print STDERR $text, $NL;
887 print STDERR $text, ' 'x ($last_message_length-$thislength), $NL;
889 $last_message_length = $nl ? 0 : $thislength;
894 if ($last_message_length) {
895 print STDERR ' ' x $last_message_length, "\r";
896 $last_message_length = 0;