]> sjero.net Git - wget/commitdiff
[svn] Big progress bar update.
authorhniksic <devnull@localhost>
Fri, 23 Nov 2001 04:59:52 +0000 (20:59 -0800)
committerhniksic <devnull@localhost>
Fri, 23 Nov 2001 04:59:52 +0000 (20:59 -0800)
Published in <sxsn11ehyn1.fsf@florida.arsdigita.de>.

12 files changed:
ChangeLog
configure.in
src/ChangeLog
src/Makefile.in
src/config.h.in
src/init.c
src/main.c
src/progress.c [new file with mode: 0644]
src/progress.h [new file with mode: 0644]
src/retr.c
src/utils.c
src/wget.h

index 37cb4c0da367fde86446616c0ed4194efa8358b7..0af5dd2ad9d38eab3a5ba077ed3774710188e13e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
index be7d112177541775b244c7ac7013497b34eadf0b..079a343873d53ecb5712bea42c878f98563e3c26 100644 (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
index 8c799ddafbef8d71b3d2a59e7ca8e31e2c01d91d..f1291b24985febbf22775dd362afd6097816a758 100644 (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.
index 78d19f151f4a540e777db554bb960262219ecde3..942e0e1e327bce8a16bdccb5315b1cea504f005a 100644 (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:
index 67cc51378a868b8f0bd9287a96bba46baaaef0cc..189d614f8b7127b85579ab9993ff6d670e7f702f 100644 (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
 
index ac3fc2e4e70df780640ff9a2e176eef6300beb05..76fbc45d9244c487e8ab4829ffbab298e1baac03 100644 (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)
 {
index 4eed7df9e1fcff4e4c81cfe12e6d76ce005c44d0..b5bbc1e580aade719fba33be1a14b59593c0eebb 100644 (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;
 }
 \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)
 {
diff --git a/src/progress.c b/src/progress.c
new file mode 100644 (file)
index 0000000..fc7ced3
--- /dev/null
@@ -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);
+}
+\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;
+}
diff --git a/src/progress.h b/src/progress.h
new file mode 100644 (file)
index 0000000..50cd211
--- /dev/null
@@ -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));
index 75ae3252d1911a1ea97c8dff19701fcefdcac1f7..9b8ddc214ad74f429ccd45cca6fdbfe1478bf38d 100644 (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;
 
 \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
@@ -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;
-}
 \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).  */
@@ -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. */
index 01cc422ee1d33898920ab1882985be861a9d047a..fca15800248351052a573cb2582eb8eb466a91a1 100644 (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 */
+}
index f0cabd16c4e4c614d0d1b6f5094a398a5399ae66..99253a74281aaa1e5a56cb8553b0e005b51e9aad 100644 (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