[svn] Big progress bar update.

Published in <sxsn11ehyn1.fsf@florida.arsdigita.de>.
This commit is contained in:
hniksic 2001-11-22 20:59:52 -08:00
parent 4d352013ac
commit cdcf67a5bd
12 changed files with 740 additions and 203 deletions

View File

@ -1,3 +1,7 @@
2001-11-23 Hrvoje Niksic <hniksic@arsdigita.com>
* configure.in: Check for sys/ioctl.h.
2001-11-22 Herold Heiko <Heiko.Herold@previnet.it>
* windows/Readme

View File

@ -155,7 +155,7 @@ dnl
dnl Checks for headers
dnl
AC_CHECK_HEADERS(string.h stdarg.h unistd.h sys/time.h utime.h sys/utime.h)
AC_CHECK_HEADERS(sys/select.h sys/utsname.h pwd.h signal.h)
AC_CHECK_HEADERS(sys/ioctl.h sys/select.h sys/utsname.h pwd.h signal.h)
AC_HEADER_TIME
dnl

View File

@ -1,3 +1,31 @@
2001-11-23 Hrvoje Niksic <hniksic@arsdigita.com>
* utils.c (determine_screen_width): New function.
* main.c (main): New option `--progress=TYPE'.
(main): Implement compatibility with the old option `--dot-style'.
* init.c: Removed cmd_spec_dotstyle -- that logic is now in
dp_set_params.
(cmd_spec_progress): New function.
* retr.c (get_contents): Use the progress_* functions instead of
the old show_progress().
(show_progress): Removed.
(rate): Print "xxxx.xx K/s" instead of "KB/s". Ditto for MB/s,
etc.
* progress.c (set_progress_implementation): New function.
(valid_progress_implementation_p): Ditto.
(progress_create): Ditto.
(progress_update): Ditto.
(progress_finish): Ditto.
(dp_create): Ditto.
(dp_update): Ditto.
(dp_finish): Ditto.
(dp_set_params): Ditto.
(print_elapsed): Ditto.
2001-11-22 Hrvoje Niksic <hniksic@arsdigita.com>
* retr.c (show_progress): Use it.

View File

@ -65,8 +65,8 @@ GETOPT_OBJ = @GETOPT_OBJ@
OBJ = $(ALLOCA) cmpt$o connect$o cookies$o fnmatch$o ftp$o \
ftp-basic$o ftp-ls$o $(OPIE_OBJ) $(GETOPT_OBJ) hash$o \
headers$o host$o html-parse$o html-url$o http$o init$o \
log$o main$o $(MD5_OBJ) netrc$o rbuf$o recur$o res$o \
retr$o safe-ctype$o snprintf$o $(SSL_OBJ) url$o \
log$o main$o $(MD5_OBJ) netrc$o progress$o rbuf$o recur$o \
res$o retr$o safe-ctype$o snprintf$o $(SSL_OBJ) url$o \
utils$o version$o
.SUFFIXES:

View File

@ -180,6 +180,9 @@ char *alloca ();
/* Define if you have the <sys/utime.h> header file. */
#undef HAVE_SYS_UTIME_H
/* Define if you have the <sys/ioctl.h> header file. */
#undef HAVE_SYS_IOCTL_H
/* Define if you have the <sys/select.h> header file. */
#undef HAVE_SYS_SELECT_H

View File

@ -82,10 +82,10 @@ CMD_DECLARE (cmd_time);
CMD_DECLARE (cmd_vector);
CMD_DECLARE (cmd_spec_dirstruct);
CMD_DECLARE (cmd_spec_dotstyle);
CMD_DECLARE (cmd_spec_header);
CMD_DECLARE (cmd_spec_htmlify);
CMD_DECLARE (cmd_spec_mirror);
CMD_DECLARE (cmd_spec_progress);
CMD_DECLARE (cmd_spec_recursive);
CMD_DECLARE (cmd_spec_useragent);
@ -122,7 +122,6 @@ static struct {
{ "dotbytes", &opt.dot_bytes, cmd_bytes },
{ "dotsinline", &opt.dots_in_line, cmd_number },
{ "dotspacing", &opt.dot_spacing, cmd_number },
{ "dotstyle", NULL, cmd_spec_dotstyle },
{ "excludedirectories", &opt.excludes, cmd_directory_vector },
{ "excludedomains", &opt.exclude_domains, cmd_vector },
{ "followftp", &opt.follow_ftp, cmd_boolean },
@ -156,6 +155,7 @@ static struct {
{ "pagerequisites", &opt.page_requisites, cmd_boolean },
{ "passiveftp", &opt.ftp_pasv, cmd_lockable_boolean },
{ "passwd", &opt.ftp_pass, cmd_string },
{ "progress", NULL, cmd_spec_progress },
{ "proxypasswd", &opt.proxy_passwd, cmd_string },
{ "proxyuser", &opt.proxy_user, cmd_string },
{ "quiet", &opt.quiet, cmd_boolean },
@ -249,6 +249,7 @@ defaults (void)
opt.remove_listing = 1;
set_progress_implementation ("dot");
opt.dot_bytes = 1024;
opt.dot_spacing = 10;
opt.dots_in_line = 50;
@ -870,61 +871,6 @@ cmd_spec_dirstruct (const char *com, const char *val, void *closure)
return 1;
}
static int
cmd_spec_dotstyle (const char *com, const char *val, void *closure)
{
/* Retrieval styles. */
if (!strcasecmp (val, "default"))
{
/* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
line. */
opt.dot_bytes = 1024;
opt.dot_spacing = 10;
opt.dots_in_line = 50;
}
else if (!strcasecmp (val, "binary"))
{
/* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
(384K) in a line. */
opt.dot_bytes = 8192;
opt.dot_spacing = 16;
opt.dots_in_line = 48;
}
else if (!strcasecmp (val, "mega"))
{
/* "Mega" retrieval, for retrieving very long files; each dot is
64K, 8 dots in a cluster, 6 clusters (3M) in a line. */
opt.dot_bytes = 65536L;
opt.dot_spacing = 8;
opt.dots_in_line = 48;
}
else if (!strcasecmp (val, "giga"))
{
/* "Giga" retrieval, for retrieving very very *very* long files;
each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
line. */
opt.dot_bytes = (1L << 20);
opt.dot_spacing = 8;
opt.dots_in_line = 32;
}
else if (!strcasecmp (val, "micro"))
{
/* "Micro" retrieval, for retrieving very small files (and/or
slow connections); each dot is 128 bytes, 8 dots in a
cluster, 6 clusters (6K) in a line. */
opt.dot_bytes = 128;
opt.dot_spacing = 8;
opt.dots_in_line = 48;
}
else
{
fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
exec_name, com, val);
return 0;
}
return 1;
}
static int
cmd_spec_header (const char *com, const char *val, void *closure)
{
@ -984,6 +930,19 @@ cmd_spec_mirror (const char *com, const char *val, void *closure)
return 1;
}
static int
cmd_spec_progress (const char *com, const char *val, void *closure)
{
if (!valid_progress_implementation_p (val))
{
fprintf (stderr, _("%s: %s: Invalid progress type `%s'.\n"),
exec_name, com, val);
return 0;
}
set_progress_implementation (val);
return 1;
}
static int
cmd_spec_recursive (const char *com, const char *val, void *closure)
{

View File

@ -51,6 +51,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "host.h"
#include "cookies.h"
#include "url.h"
#include "progress.h" /* for progress_handle_sigwinch */
/* On GNU system this will include system-wide getopt.h. */
#include "getopt.h"
@ -165,7 +166,7 @@ Download:\n\
-O --output-document=FILE write documents to FILE.\n\
-nc, --no-clobber don\'t clobber existing files or use .# suffixes.\n\
-c, --continue resume getting a partially-downloaded file.\n\
--dot-style=STYLE set retrieval display style.\n\
--progress=TYPE select progress gauge type.\n\
-N, --timestamping don\'t re-retrieve files unless newer than local.\n\
-S, --server-response print server response.\n\
--spider don\'t download anything.\n\
@ -312,6 +313,7 @@ main (int argc, char *const *argv)
{ "no", required_argument, NULL, 'n' },
{ "output-document", required_argument, NULL, 'O' },
{ "output-file", required_argument, NULL, 'o' },
{ "progress", required_argument, NULL, 163 },
{ "proxy", required_argument, NULL, 'Y' },
{ "proxy-passwd", required_argument, NULL, 144 },
{ "proxy-user", required_argument, NULL, 143 },
@ -499,7 +501,13 @@ GNU General Public License for more details.\n"));
setval ("header", optarg);
break;
case 134:
setval ("dotstyle", optarg);
/* Supported for compatibility; --dot-style=foo equivalent
to --progress=dot:foo. */
{
char *tmp = alloca (3 + 1 + strlen (optarg));
sprintf (tmp, "dot:%s", optarg);
setval ("progress", tmp);
}
break;
case 135:
setval ("htmlify", optarg);
@ -531,6 +539,9 @@ GNU General Public License for more details.\n"));
case 162:
setval ("savecookies", optarg);
break;
case 163:
setval ("progress", optarg);
break;
case 157:
setval ("referer", optarg);
break;
@ -784,6 +795,9 @@ Can't timestamp and not clobber old files at the same time.\n"));
process exits. What we want is to ignore SIGPIPE and just check
for the return value of write(). */
signal (SIGPIPE, SIG_IGN);
#ifdef SIGWINCH
signal (SIGWINCH, progress_handle_sigwinch);
#endif
#endif /* HAVE_SIGNAL */
status = RETROK; /* initialize it, just-in-case */
@ -860,11 +874,11 @@ Can't timestamp and not clobber old files at the same time.\n"));
return 1;
}
#ifdef HAVE_SIGNAL
/* Hangup signal handler. When wget receives SIGHUP or SIGUSR1, it
will proceed operation as usual, trying to write into a log file.
If that is impossible, the output will be turned off. */
#ifdef HAVE_SIGNAL
static RETSIGTYPE
redirect_output_signal (int sig)
{

584
src/progress.c Normal file
View File

@ -0,0 +1,584 @@
/* Download progress.
Copyright (C) 2001 Free Software Foundation, Inc.
This file is part of GNU Wget.
GNU Wget is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
\(at your option) any later version.
GNU Wget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wget; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif /* HAVE_STRING_H */
#include <assert.h>
#include "wget.h"
#include "progress.h"
#include "utils.h"
#include "retr.h"
struct progress_implementation {
char *name;
void *(*create) (long, long);
void (*update) (void *, long);
void (*finish) (void *);
void (*set_params) (const char *);
};
/* Necessary forward declarations. */
static void *dp_create PARAMS ((long, long));
static void dp_update PARAMS ((void *, long));
static void dp_finish PARAMS ((void *));
static void dp_set_params PARAMS ((const char *));
static void *bar_create PARAMS ((long, long));
static void bar_update PARAMS ((void *, long));
static void bar_finish PARAMS ((void *));
static void bar_set_params PARAMS ((const char *));
static struct progress_implementation implementations[] = {
{ "dot", dp_create, dp_update, dp_finish, dp_set_params },
{ "bar", bar_create, bar_update, bar_finish, bar_set_params }
};
static struct progress_implementation *current_impl;
/* Return non-zero if NAME names a valid progress bar implementation.
The characters after the first : will be ignored. */
int
valid_progress_implementation_p (const char *name)
{
int i = 0;
struct progress_implementation *pi = implementations;
char *colon = strchr (name, ':');
int namelen = colon ? colon - name : strlen (name);
for (i = 0; i < ARRAY_SIZE (implementations); i++, pi++)
if (!strncmp (pi->name, name, namelen))
return 1;
return 0;
}
/* Set the progress implementation to NAME. */
void
set_progress_implementation (const char *name)
{
int i = 0;
struct progress_implementation *pi = implementations;
char *colon = strchr (name, ':');
int namelen = colon ? colon - name : strlen (name);
for (i = 0; i < ARRAY_SIZE (implementations); i++, pi++)
if (!strncmp (pi->name, name, namelen))
{
current_impl = pi;
if (colon)
/* We call pi->set_params even if colon is NULL because we
want to give the implementation a chance to set up some
things it needs to run. */
++colon;
if (pi->set_params)
pi->set_params (colon);
return;
}
abort ();
}
/* Create a progress gauge. INITIAL is the number of bytes the
download starts from (zero if the download starts from scratch).
TOTAL is the expected total number of bytes in this download. If
TOTAL is zero, it means that the download size is not known in
advance. */
void *
progress_create (long initial, long total)
{
return current_impl->create (initial, total);
}
/* Inform the progress gauge of newly received bytes. */
void
progress_update (void *progress, long howmuch)
{
current_impl->update (progress, howmuch);
}
/* Tell the progress gauge to clean up. Calling this will free the
PROGRESS object, the further use of which is not allowed. */
void
progress_finish (void *progress)
{
current_impl->finish (progress);
}
/* Dot-printing. */
struct dot_progress {
long initial_length; /* how many bytes have been downloaded
previously. */
long total_length; /* expected total byte count when the
download finishes */
int accumulated;
int rows; /* number of rows printed so far */
int dots; /* number of dots printed in this row */
struct wget_timer *timer; /* timer used to measure per-row
download rates. */
long last_timer_value;
};
/* Dot-progress backend for progress_create. */
static void *
dp_create (long initial, long total)
{
struct dot_progress *dp = xmalloc (sizeof (struct dot_progress));
memset (dp, 0, sizeof (*dp));
dp->initial_length = initial;
dp->total_length = total;
dp->timer = wtimer_new ();
if (dp->initial_length)
{
int dot_bytes = opt.dot_bytes;
long row_bytes = opt.dot_bytes * opt.dots_in_line;
int remainder = (int) (dp->initial_length % row_bytes);
long skipped = dp->initial_length - remainder;
if (skipped)
{
logputs (LOG_VERBOSE, "\n "); /* leave spacing untranslated */
logprintf (LOG_VERBOSE, _("[ skipping %dK ]"),
(int) (skipped / 1024));
}
logprintf (LOG_VERBOSE, "\n%5ldK", skipped / 1024);
for (; remainder >= dot_bytes; remainder -= dot_bytes)
{
if (dp->dots % opt.dot_spacing == 0)
logputs (LOG_VERBOSE, " ");
logputs (LOG_VERBOSE, ",");
++dp->dots;
}
assert (dp->dots < opt.dots_in_line);
dp->accumulated = remainder;
dp->rows = skipped / row_bytes;
}
return dp;
}
static void
print_percentage (long bytes, long expected)
{
int percentage = (int)(100.0 * bytes / expected);
logprintf (LOG_VERBOSE, "%3d%%", percentage);
}
static void
print_elapsed (struct dot_progress *dp, long bytes)
{
long timer_value = wtimer_elapsed (dp->timer);
logprintf (LOG_VERBOSE, " @ %s",
rate (bytes, timer_value - dp->last_timer_value, 1));
dp->last_timer_value = timer_value;
}
/* Dot-progress backend for progress_update. */
static void
dp_update (void *progress, long howmuch)
{
struct dot_progress *dp = progress;
int dot_bytes = opt.dot_bytes;
long row_bytes = opt.dot_bytes * opt.dots_in_line;
log_set_flush (0);
dp->accumulated += howmuch;
for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
{
if (dp->dots == 0)
logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
if (dp->dots % opt.dot_spacing == 0)
logputs (LOG_VERBOSE, " ");
logputs (LOG_VERBOSE, ".");
++dp->dots;
if (dp->dots >= opt.dots_in_line)
{
++dp->rows;
dp->dots = 0;
if (dp->total_length)
print_percentage (dp->rows * row_bytes, dp->total_length);
print_elapsed (dp, row_bytes - (dp->initial_length % row_bytes));
}
}
log_set_flush (1);
}
/* Dot-progress backend for progress_finish. */
static void
dp_finish (void *progress)
{
struct dot_progress *dp = progress;
int dot_bytes = opt.dot_bytes;
long row_bytes = opt.dot_bytes * opt.dots_in_line;
int i;
log_set_flush (0);
for (i = dp->dots; i < opt.dots_in_line; i++)
{
if (i % opt.dot_spacing == 0)
logputs (LOG_VERBOSE, " ");
logputs (LOG_VERBOSE, " ");
}
if (dp->total_length)
{
print_percentage (dp->rows * row_bytes
+ dp->dots * dot_bytes
+ dp->accumulated,
dp->total_length);
}
print_elapsed (dp, dp->dots * dot_bytes
+ dp->accumulated
- dp->initial_length % row_bytes);
logputs (LOG_VERBOSE, "\n\n");
log_set_flush (0);
wtimer_delete (dp->timer);
xfree (dp);
}
/* This function interprets the progress "parameters". For example,
if Wget is invoked with --progress=bar:mega, it will set the
"dot-style" to "mega". Valid styles are default, binary, mega, and
giga. */
static void
dp_set_params (const char *params)
{
if (!params)
return;
/* We use this to set the retrieval style. */
if (!strcasecmp (params, "default"))
{
/* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
line. */
opt.dot_bytes = 1024;
opt.dot_spacing = 10;
opt.dots_in_line = 50;
}
else if (!strcasecmp (params, "binary"))
{
/* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
(384K) in a line. */
opt.dot_bytes = 8192;
opt.dot_spacing = 16;
opt.dots_in_line = 48;
}
else if (!strcasecmp (params, "mega"))
{
/* "Mega" retrieval, for retrieving very long files; each dot is
64K, 8 dots in a cluster, 6 clusters (3M) in a line. */
opt.dot_bytes = 65536L;
opt.dot_spacing = 8;
opt.dots_in_line = 48;
}
else if (!strcasecmp (params, "giga"))
{
/* "Giga" retrieval, for retrieving very very *very* long files;
each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
line. */
opt.dot_bytes = (1L << 20);
opt.dot_spacing = 8;
opt.dots_in_line = 32;
}
else
fprintf (stderr,
_("Invalid dot style specification `%s'; leaving unchanged.\n"),
params);
}
/* "Thermometer" (bar) progress. */
/* Assumed screen width if we can't find the real value. */
#define DEFAULT_SCREEN_WIDTH 80
/* Minimum screen width we'll try to work with. If this is too small,
create_image will overflow the buffer. */
#define MINIMUM_SCREEN_WIDTH 45
static int screen_width = DEFAULT_SCREEN_WIDTH;
struct bar_progress {
long initial_length; /* how many bytes have been downloaded
previously. */
long total_length; /* expected total byte count when the
download finishes */
long count; /* bytes downloaded so far */
struct wget_timer *timer; /* timer used to measure the download
rates. */
long last_update; /* time of the last screen update. */
int width; /* screen width at the time the
progress gauge was created. */
char *buffer; /* buffer where the bar "image" is
stored. */
};
static void create_image PARAMS ((struct bar_progress *, long));
static void display_image PARAMS ((char *));
static void *
bar_create (long initial, long total)
{
struct bar_progress *bp = xmalloc (sizeof (struct bar_progress));
memset (bp, 0, sizeof (*bp));
bp->initial_length = initial;
bp->total_length = total;
bp->timer = wtimer_new ();
bp->width = screen_width;
bp->buffer = xmalloc (bp->width + 1);
logputs (LOG_VERBOSE, "\n");
create_image (bp, 0);
display_image (bp->buffer);
return bp;
}
static void
bar_update (void *progress, long howmuch)
{
struct bar_progress *bp = progress;
int force_update = 0;
long dltime = wtimer_elapsed (bp->timer);
bp->count += howmuch;
if (screen_width != bp->width)
{
bp->width = screen_width;
bp->buffer = xrealloc (bp->buffer, bp->width + 1);
}
if (dltime - bp->last_update < 200 && !force_update)
/* Don't update more often than every half a second. */
return;
bp->last_update = dltime;
create_image (bp, dltime);
display_image (bp->buffer);
}
static void
bar_finish (void *progress)
{
struct bar_progress *bp = progress;
create_image (bp, wtimer_elapsed (bp->timer));
display_image (bp->buffer);
logputs (LOG_VERBOSE, "\n\n");
xfree (bp->buffer);
wtimer_delete (bp->timer);
xfree (bp);
}
static void
create_image (struct bar_progress *bp, long dltime)
{
char *p = bp->buffer;
long size = bp->initial_length + bp->count;
/* The progress bar should look like this:
xxx% |=======> | xx KB/s nnnnn ETA: 00:00
Calculate its geometry:
"xxx% " - percentage - 5 chars
"| ... | " - progress bar decorations - 3 chars
"1234.56 K/s " - dl rate - 12 chars
"nnnn " - downloaded bytes - 11 chars
"ETA: xx:xx:xx" - ETA - 13 chars
"=====>..." - progress bar content - the rest
*/
int progress_len = screen_width - (5 + 3 + 12 + 11 + 13);
if (progress_len < 7)
progress_len = 0;
/* "xxx% " */
if (bp->total_length > 0)
{
int percentage = (int)(100.0 * size / bp->total_length);
assert (percentage <= 100);
sprintf (p, "%3d%% ", percentage);
p += 5;
}
/* The progress bar: "|====> | " */
if (progress_len && bp->total_length > 0)
{
double fraction = (double)size / bp->total_length;
int dlsz = (int)(fraction * progress_len);
char *begin;
assert (dlsz <= progress_len);
*p++ = '|';
begin = p;
if (dlsz > 0)
{
/* Draw dlsz-1 '=' chars and one arrow char. */
while (dlsz-- > 1)
*p++ = '=';
*p++ = '>';
}
while (p - begin < progress_len)
*p++ = ' ';
*p++ = '|';
*p++ = ' ';
}
/* "2.3 KB/s " */
if (dltime && bp->count)
{
char *rt = rate (bp->count, dltime, 1);
strcpy (p, rt);
p += strlen (p);
*p++ = ' ';
}
else
{
strcpy (p, "----.-- KB/s ");
p += 13;
}
/* "12376 " */
sprintf (p, _("%ld "), size);
p += strlen (p);
/* "ETA: xx:xx:xx" */
if (bp->total_length > 0 && bp->count > 0)
{
int eta, eta_hrs, eta_min, eta_sec;
double tm_sofar = (double)dltime / 1000;
long bytes_remaining = bp->total_length - size;
eta = (int) (tm_sofar * bytes_remaining / bp->count);
eta_hrs = eta / 3600, eta %= 3600;
eta_min = eta / 60, eta %= 60;
eta_sec = eta;
/*printf ("\neta: %d, %d %d %d\n", eta, eta_hrs, eta_min, eta_sec);*/
/*printf ("\n%ld %f %ld %ld\n", dltime, tm_sofar, bytes_remaining, bp->count);*/
*p++ = 'E';
*p++ = 'T';
*p++ = 'A';
*p++ = ':';
*p++ = ' ';
if (eta_hrs > 99)
/* Bogus value, for whatever reason. We must avoid overflow. */
sprintf (p, "--:--");
else if (eta_hrs > 0)
sprintf (p, "%d:%02d:%02d", eta_hrs, eta_min, eta_sec);
else
sprintf (p, "%02d:%02d", eta_min, eta_sec);
p += strlen (p);
}
else if (bp->total_length > 0)
{
strcpy (p, "ETA: --:--");
p += 10;
}
assert (p - bp->buffer <= screen_width);
while (p < bp->buffer + screen_width)
*p++ = ' ';
*p = '\0';
}
static void
display_image (char *buf)
{
int len = strlen (buf);
char *del_buf = alloca (len + 1);
logputs (LOG_VERBOSE, buf);
memset (del_buf, '\b', len);
del_buf[len] = '\0';
logputs (LOG_VERBOSE, del_buf);
}
static void
bar_set_params (const char *ignored)
{
int sw = determine_screen_width ();
if (sw && sw >= MINIMUM_SCREEN_WIDTH)
screen_width = sw;
}
RETSIGTYPE
progress_handle_sigwinch (int sig)
{
int sw = determine_screen_width ();
if (sw && sw >= MINIMUM_SCREEN_WIDTH)
screen_width = sw;
}

27
src/progress.h Normal file
View File

@ -0,0 +1,27 @@
/* Download progress.
Copyright (C) 2001 Free Software Foundation, Inc.
This file is part of GNU Wget.
GNU Wget is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
\(at your option) any later version.
GNU Wget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wget; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
int valid_progress_implementation_p PARAMS ((const char *));
void set_progress_implementation PARAMS ((const char *));
void *progress_create PARAMS ((long, long));
void progress_update PARAMS ((void *, long));
void progress_finish PARAMS ((void *));
RETSIGTYPE progress_handle_sigwinch PARAMS ((int));

View File

@ -36,6 +36,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "wget.h"
#include "utils.h"
#include "retr.h"
#include "progress.h"
#include "url.h"
#include "recur.h"
#include "ftp.h"
@ -51,11 +52,6 @@ extern int errno;
int global_download_count;
/* Flags for show_progress(). */
enum spflags { SP_NONE, SP_INIT, SP_FINISH };
static int show_progress PARAMS ((long, long, enum spflags));
#define MIN(i, j) ((i) <= (j) ? (i) : (j))
/* Reads the contents of file descriptor FD, until it is closed, or a
@ -85,23 +81,28 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
{
int res = 0;
static char c[8192];
void *progress = NULL;
*len = restval;
if (opt.verbose)
show_progress (restval, expected, SP_INIT);
progress = progress_create (restval, expected);
if (rbuf && RBUF_FD (rbuf) == fd)
{
int need_flush = 0;
while ((res = rbuf_flush (rbuf, c, sizeof (c))) != 0)
{
if (fwrite (c, sizeof (char), res, fp) < res)
return -2;
if (opt.verbose)
{
if (show_progress (res, expected, SP_NONE))
fflush (fp);
}
progress_update (progress, res);
*len += res;
need_flush = 1;
}
if (need_flush)
fflush (fp);
if (ferror (fp))
return -2;
}
/* Read from fd while there is available data.
@ -124,13 +125,15 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
#endif /* HAVE_SSL */
if (res > 0)
{
if (fwrite (c, sizeof (char), res, fp) < res)
fwrite (c, sizeof (char), res, fp);
/* Always flush the contents of the network packet. This
should not be adverse to performance, as the network
packets typically won't be too tiny anyway. */
fflush (fp);
if (ferror (fp))
return -2;
if (opt.verbose)
{
if (show_progress (res, expected, SP_NONE))
fflush (fp);
}
progress_update (progress, res);
*len += res;
}
else
@ -139,132 +142,14 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
if (res < -1)
res = -1;
if (opt.verbose)
show_progress (0, expected, SP_FINISH);
progress_finish (progress);
return res;
}
static void
print_percentage (long bytes, long expected)
{
int percentage = (int)(100.0 * bytes / expected);
logprintf (LOG_VERBOSE, "%3d%%", percentage);
}
/* Show the dotted progress report of file loading. Called with
length and a flag to tell it whether to reset or not. It keeps the
offset information in static local variables.
Return value: 1 or 0, designating whether any dots have been drawn.
If the init argument is set, the routine will initialize.
If the res is non-zero, res/line_bytes lines are skipped
(meaning the appropriate number ok kilobytes), and the number of
"dots" fitting on the first line are drawn as ','. */
static int
show_progress (long res, long expected, enum spflags flags)
{
static struct wget_timer *timer;
static long line_bytes;
static long offs, initial_skip;
static int ndot, nrow;
static long last_timer_value, time_offset;
int any_output = 0;
if (flags == SP_FINISH)
{
int dot = ndot;
char *tmpstr = (char *)alloca (2 * opt.dots_in_line + 1);
char *tmpp = tmpstr;
time_offset = wtimer_elapsed (timer) - last_timer_value;
for (; dot < opt.dots_in_line; dot++)
{
if (!(dot % opt.dot_spacing))
*tmpp++ = ' ';
*tmpp++ = ' ';
}
*tmpp = '\0';
logputs (LOG_VERBOSE, tmpstr);
if (expected)
print_percentage (nrow * line_bytes + ndot * opt.dot_bytes + offs,
expected);
logprintf (LOG_VERBOSE, " @%s",
rate (ndot * opt.dot_bytes
+ offs - (initial_skip % line_bytes),
time_offset, 1));
logputs (LOG_VERBOSE, "\n\n");
return 0;
}
/* Temporarily disable flushing. */
log_set_flush (0);
/* init set means initialization. If res is set, it also means that
the retrieval is *not* done from the beginning. The part that
was already retrieved is not shown again. */
if (flags == SP_INIT)
{
/* Generic initialization of static variables. */
offs = 0L;
ndot = nrow = 0;
line_bytes = (long)opt.dots_in_line * opt.dot_bytes;
if (!timer)
timer = wtimer_allocate ();
wtimer_reset (timer);
last_timer_value = 0;
time_offset = 0;
initial_skip = res;
if (res)
{
if (res >= line_bytes)
{
nrow = res / line_bytes;
res %= line_bytes;
logprintf (LOG_VERBOSE,
_("\n [ skipping %dK ]"),
(int) ((nrow * line_bytes) / 1024));
ndot = 0;
}
}
logprintf (LOG_VERBOSE, "\n%5ldK", nrow * line_bytes / 1024);
}
/* Offset gets incremented by current value. */
offs += res;
/* While offset is >= opt.dot_bytes, print dots, taking care to
precede every 50th dot with a status message. */
for (; offs >= opt.dot_bytes; offs -= opt.dot_bytes)
{
if (!(ndot % opt.dot_spacing))
logputs (LOG_VERBOSE, " ");
any_output = 1;
logputs (LOG_VERBOSE, flags == SP_INIT ? "," : ".");
++ndot;
if (ndot == opt.dots_in_line)
{
time_offset = wtimer_elapsed (timer) - last_timer_value;
last_timer_value += time_offset;
ndot = 0;
++nrow;
if (expected)
print_percentage (nrow * line_bytes, expected);
logprintf (LOG_VERBOSE, " @%s",
rate (line_bytes - (initial_skip % line_bytes),
time_offset, 1));
initial_skip = 0;
logprintf (LOG_VERBOSE, "\n%5ldK", nrow * line_bytes / 1024);
}
}
/* Reenable flushing. */
log_set_flush (1);
return any_output;
}
/* Print out the appropriate download rate. Appropriate means that if
rate is > 1024 bytes per second, kilobytes are used, and if rate >
1024 * 1024 bps, megabytes are used.
/* Return a printed representation of the download rate, as
appropriate for the speed. Appropriate means that if rate is
greater than 1K/s, kilobytes are used, and if rate is greater than
1MB/s, megabytes are used.
If PAD is non-zero, strings will be padded to the width of 7
characters (xxxx.xx). */
@ -274,6 +159,9 @@ rate (long bytes, long msecs, int pad)
static char res[15];
double dlrate;
assert (msecs >= 0);
assert (bytes >= 0);
if (msecs == 0)
/* If elapsed time is 0, it means we're under the granularity of
the timer. This often happens on systems that use time() for
@ -284,9 +172,9 @@ rate (long bytes, long msecs, int pad)
if (dlrate < 1024.0)
sprintf (res, pad ? "%7.2f B/s" : "%.2f B/s", dlrate);
else if (dlrate < 1024.0 * 1024.0)
sprintf (res, pad ? "%7.2f KB/s" : "%.2f KB/s", dlrate / 1024.0);
sprintf (res, pad ? "%7.2f K/s" : "%.2f K/s", dlrate / 1024.0);
else if (dlrate < 1024.0 * 1024.0 * 1024.0)
sprintf (res, pad ? "%7.2f MB/s" : "%.2f MB/s", dlrate / (1024.0 * 1024.0));
sprintf (res, pad ? "%7.2f M/s" : "%.2f M/s", dlrate / (1024.0 * 1024.0));
else
/* Maybe someone will need this one day. More realistically, it
will get tickled by buggy timers. */

View File

@ -50,6 +50,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#endif
#include <fcntl.h>
#include <assert.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include "wget.h"
#include "utils.h"
@ -1704,3 +1707,28 @@ html_quote_string (const char *s)
*p = '\0';
return res;
}
/* Determine the width of the terminal we're running on. If that's
not possible, return 0. */
int
determine_screen_width (void)
{
/* If there's a way to get the terminal size using POSIX
tcgetattr(), somebody please tell me. */
#ifndef TIOCGWINSZ
return 0;
#else /* TIOCGWINSZ */
int fd;
struct winsize wsz;
if (opt.lfilename != NULL)
return 0;
fd = fileno (stderr);
if (ioctl (fd, TIOCGWINSZ, &wsz) < 0)
return 0; /* most likely ENOTTY */
return wsz.ws_col;
#endif /* TIOCGWINSZ */
}

View File

@ -28,6 +28,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
# define NDEBUG /* To kill off assertions */
#endif /* not DEBUG */
#define DEBUG_MALLOC
#ifndef PARAMS
# if PROTOTYPES
# define PARAMS(args) args