Published in <sxsn11ehyn1.fsf@florida.arsdigita.de>.
+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
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
+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.
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:
/* 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
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);
{ "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 },
{ "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 },
opt.remove_listing = 1;
+ set_progress_implementation ("dot");
opt.dot_bytes = 1024;
opt.dot_spacing = 10;
opt.dots_in_line = 50;
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)
{
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)
{
#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"
-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\
{ "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 },
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);
case 162:
setval ("savecookies", optarg);
break;
+ case 163:
+ setval ("progress", optarg);
+ break;
case 157:
setval ("referer", optarg);
break;
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 */
return 1;
}
\f
+#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)
{
--- /dev/null
+/* 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);
+}
+\f
+/* 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);
+}
+\f
+/* "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;
+}
--- /dev/null
+/* 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));
#include "wget.h"
#include "utils.h"
#include "retr.h"
+#include "progress.h"
#include "url.h"
#include "recur.h"
#include "ftp.h"
int global_download_count;
\f
-/* 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
{
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.
#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
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;
-}
\f
-/* 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). */
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
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. */
#endif
#include <fcntl.h>
#include <assert.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
#include "wget.h"
#include "utils.h"
*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 */
+}
# define NDEBUG /* To kill off assertions */
#endif /* not DEBUG */
+#define DEBUG_MALLOC
+
#ifndef PARAMS
# if PROTOTYPES
# define PARAMS(args) args