From cdcf67a5bdae9c56d263ebf7608b52701851cf22 Mon Sep 17 00:00:00 2001 From: hniksic Date: Thu, 22 Nov 2001 20:59:52 -0800 Subject: [PATCH] [svn] Big progress bar update. Published in . --- ChangeLog | 4 + configure.in | 2 +- src/ChangeLog | 28 +++ src/Makefile.in | 4 +- src/config.h.in | 3 + src/init.c | 73 ++---- src/main.c | 20 +- src/progress.c | 584 ++++++++++++++++++++++++++++++++++++++++++++++++ src/progress.h | 27 +++ src/retr.c | 168 +++----------- src/utils.c | 28 +++ src/wget.h | 2 + 12 files changed, 740 insertions(+), 203 deletions(-) create mode 100644 src/progress.c create mode 100644 src/progress.h diff --git a/ChangeLog b/ChangeLog index 37cb4c0d..0af5dd2a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2001-11-23 Hrvoje Niksic + + * configure.in: Check for sys/ioctl.h. + 2001-11-22 Herold Heiko * windows/Readme diff --git a/configure.in b/configure.in index be7d1121..079a3438 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/src/ChangeLog b/src/ChangeLog index 8c799dda..f1291b24 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,31 @@ +2001-11-23 Hrvoje Niksic + + * 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 * retr.c (show_progress): Use it. diff --git a/src/Makefile.in b/src/Makefile.in index 78d19f15..942e0e1e 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -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: diff --git a/src/config.h.in b/src/config.h.in index 67cc5137..189d614f 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -180,6 +180,9 @@ char *alloca (); /* Define if you have the header file. */ #undef HAVE_SYS_UTIME_H +/* Define if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + /* Define if you have the header file. */ #undef HAVE_SYS_SELECT_H diff --git a/src/init.c b/src/init.c index ac3fc2e4..76fbc45d 100644 --- a/src/init.c +++ b/src/init.c @@ -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) { diff --git a/src/main.c b/src/main.c index 4eed7df9..b5bbc1e5 100644 --- a/src/main.c +++ b/src/main.c @@ -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) { diff --git a/src/progress.c b/src/progress.c new file mode 100644 index 00000000..fc7ced31 --- /dev/null +++ b/src/progress.c @@ -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 + +#include +#include +#ifdef HAVE_STRING_H +# include +#else +# include +#endif /* HAVE_STRING_H */ +#include + +#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; +} diff --git a/src/progress.h b/src/progress.h new file mode 100644 index 00000000..50cd2116 --- /dev/null +++ b/src/progress.h @@ -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)); diff --git a/src/retr.c b/src/retr.c index 75ae3252..9b8ddc21 100644 --- a/src/retr.c +++ b/src/retr.c @@ -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. */ diff --git a/src/utils.c b/src/utils.c index 01cc422e..fca15800 100644 --- a/src/utils.c +++ b/src/utils.c @@ -50,6 +50,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #endif #include #include +#ifdef HAVE_SYS_IOCTL_H +# include +#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 */ +} diff --git a/src/wget.h b/src/wget.h index f0cabd16..99253a74 100644 --- a/src/wget.h +++ b/src/wget.h @@ -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