]> sjero.net Git - wget/blobdiff - src/progress.c
[svn] Resurrect opt.dot_style.
[wget] / src / progress.c
index fc7ced31a881b5d7fbc605bbbd265e4f6f34b45b..18732c77d794d6c7ebf23465cbca717ab31a4c6d 100644 (file)
@@ -6,7 +6,7 @@ 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.
+(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
@@ -27,6 +27,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 # include <strings.h>
 #endif /* HAVE_STRING_H */
 #include <assert.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
 
 #include "wget.h"
 #include "progress.h"
@@ -36,29 +39,42 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 struct progress_implementation {
   char *name;
   void *(*create) (long, long);
-  void (*update) (void *, long);
-  void (*finish) (void *);
+  void (*update) (void *, long, long);
+  void (*finish) (void *, long);
   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 *dot_create PARAMS ((long, long));
+static void dot_update PARAMS ((void *, long, long));
+static void dot_finish PARAMS ((void *, long));
+static void dot_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_update PARAMS ((void *, long, long));
+static void bar_finish PARAMS ((void *, long));
 static void bar_set_params PARAMS ((const char *));
 
 static struct progress_implementation implementations[] = {
-  { "dot", dp_create, dp_update, dp_finish, dp_set_params },
+  { "dot", dot_create, dot_update, dot_finish, dot_set_params },
   { "bar", bar_create, bar_update, bar_finish, bar_set_params }
 };
 static struct progress_implementation *current_impl;
 
+/* Progress implementation used by default.  Can be overriden in
+   wgetrc or by the fallback one.  */
+
+#define DEFAULT_PROGRESS_IMPLEMENTATION "bar"
+
+/* Fallnback progress implementation should be something that works
+   under all display types.  If you put something other than "dot"
+   here, remember that bar_set_params tries to switch to this if we're
+   not running on a TTY.  So changing this to "bar" could cause
+   infloop.  */
+
+#define FALLBACK_PROGRESS_IMPLEMENTATION "dot"
+
 /* Return non-zero if NAME names a valid progress bar implementation.
    The characters after the first : will be ignored.  */
 
@@ -81,10 +97,15 @@ valid_progress_implementation_p (const char *name)
 void
 set_progress_implementation (const char *name)
 {
-  int i = 0;
+  int i, namelen;
   struct progress_implementation *pi = implementations;
-  char *colon = strchr (name, ':');
-  int namelen = colon ? colon - name : strlen (name);
+  char *colon;
+
+  if (!name)
+    name = DEFAULT_PROGRESS_IMPLEMENTATION;
+
+  colon = strchr (name, ':');
+  namelen = colon ? colon - name : strlen (name);
 
   for (i = 0; i < ARRAY_SIZE (implementations); i++, pi++)
     if (!strncmp (pi->name, name, namelen))
@@ -116,21 +137,22 @@ progress_create (long initial, long total)
   return current_impl->create (initial, total);
 }
 
-/* Inform the progress gauge of newly received bytes. */
+/* Inform the progress gauge of newly received bytes.  DLTIME is the
+   time in milliseconds since the beginning of the download.  */
 
 void
-progress_update (void *progress, long howmuch)
+progress_update (void *progress, long howmuch, long dltime)
 {
-  current_impl->update (progress, howmuch);
+  current_impl->update (progress, howmuch, dltime);
 }
 
 /* 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)
+progress_finish (void *progress, long dltime)
 {
-  current_impl->finish (progress);
+  current_impl->finish (progress, dltime);
 }
 \f
 /* Dot-printing. */
@@ -145,16 +167,13 @@ struct dot_progress {
 
   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)
+dot_create (long initial, long total)
 {
   struct dot_progress *dp = xmalloc (sizeof (struct dot_progress));
 
@@ -162,7 +181,6 @@ dp_create (long initial, long total)
 
   dp->initial_length = initial;
   dp->total_length   = total;
-  dp->timer = wtimer_new ();
 
   if (dp->initial_length)
     {
@@ -174,9 +192,16 @@ dp_create (long initial, long total)
 
       if (skipped)
        {
-         logputs (LOG_VERBOSE, "\n      "); /* leave spacing untranslated */
-         logprintf (LOG_VERBOSE, _("[ skipping %dK ]"),
-                    (int) (skipped / 1024));
+         int skipped_k = (int) (skipped / 1024); /* skipped amount in K */
+         int skipped_k_len = numdigit (skipped_k);
+         if (skipped_k_len < 5)
+           skipped_k_len = 5;
+
+         /* Align the [ skipping ... ] line with the dots.  To do
+            that, insert the number of spaces equal to the number of
+            digits in the skipped amount in K.  */
+         logprintf (LOG_VERBOSE, _("\n%*s[ skipping %dK ]"),
+                    2 + skipped_k_len, "", skipped_k);
        }
 
       logprintf (LOG_VERBOSE, "\n%5ldK", skipped / 1024);
@@ -204,18 +229,17 @@ print_percentage (long bytes, long expected)
 }
 
 static void
-print_elapsed (struct dot_progress *dp, long bytes)
+print_download_speed (struct dot_progress *dp, long bytes, long dltime)
 {
-  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;
+  logprintf (LOG_VERBOSE, " %s",
+            retr_rate (bytes, dltime - dp->last_timer_value, 1));
+  dp->last_timer_value = dltime;
 }
 
 /* Dot-progress backend for progress_update. */
 
 static void
-dp_update (void *progress, long howmuch)
+dot_update (void *progress, long howmuch, long dltime)
 {
   struct dot_progress *dp = progress;
   int dot_bytes = opt.dot_bytes;
@@ -236,13 +260,16 @@ dp_update (void *progress, long howmuch)
       ++dp->dots;
       if (dp->dots >= opt.dots_in_line)
        {
+         long row_qty = row_bytes;
+         if (dp->rows == dp->initial_length / row_bytes)
+           row_qty -= dp->initial_length % row_bytes;
+
          ++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));
+         print_download_speed (dp, row_qty, dltime);
        }
     }
 
@@ -252,7 +279,7 @@ dp_update (void *progress, long howmuch)
 /* Dot-progress backend for progress_finish. */
 
 static void
-dp_finish (void *progress)
+dot_finish (void *progress, long dltime)
 {
   struct dot_progress *dp = progress;
   int dot_bytes = opt.dot_bytes;
@@ -261,6 +288,8 @@ dp_finish (void *progress)
 
   log_set_flush (0);
 
+  if (dp->dots == 0)
+    logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
   for (i = dp->dots; i < opt.dots_in_line; i++)
     {
       if (i % opt.dot_spacing == 0)
@@ -275,25 +304,30 @@ dp_finish (void *progress)
                        dp->total_length);
     }
 
-  print_elapsed (dp, dp->dots * dot_bytes
-                + dp->accumulated
-                - dp->initial_length % row_bytes);
-  logputs (LOG_VERBOSE, "\n\n");
+  {
+    long row_qty = dp->dots * dot_bytes + dp->accumulated;
+    if (dp->rows == dp->initial_length / row_bytes)
+      row_qty -= dp->initial_length % row_bytes;
+    print_download_speed (dp, row_qty, dltime);
+  }
 
+  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
+   if Wget is invoked with --progress=dot: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)
+dot_set_params (const char *params)
 {
+  if (!params || !*params)
+    params = opt.dot_style;
+
   if (!params)
     return;
 
@@ -355,14 +389,14 @@ struct bar_progress {
                                   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. */
+
+  int tick;
 };
 
 static void create_image PARAMS ((struct bar_progress *, long));
@@ -377,7 +411,6 @@ bar_create (long initial, long total)
 
   bp->initial_length = initial;
   bp->total_length   = total;
-  bp->timer = wtimer_new ();
   bp->width = screen_width;
   bp->buffer = xmalloc (bp->width + 1);
 
@@ -390,13 +423,20 @@ bar_create (long initial, long total)
 }
 
 static void
-bar_update (void *progress, long howmuch)
+bar_update (void *progress, long howmuch, long dltime)
 {
   struct bar_progress *bp = progress;
   int force_update = 0;
-  long dltime = wtimer_elapsed (bp->timer);
 
   bp->count += howmuch;
+  if (bp->total_length > 0
+      && bp->count + bp->initial_length > bp->total_length)
+    /* We could be downloading more than total_length, e.g. when the
+       server sends an incorrect Content-Length header.  In that case,
+       adjust bp->total_length to the new reality, so that the code in
+       create_image() that depends on total size being smaller or
+       equal to the expected size doesn't abort.  */
+    bp->total_length = bp->count + bp->initial_length;
 
   if (screen_width != bp->width)
     {
@@ -415,17 +455,22 @@ bar_update (void *progress, long howmuch)
 }
 
 static void
-bar_finish (void *progress)
+bar_finish (void *progress, long dltime)
 {
   struct bar_progress *bp = progress;
 
-  create_image (bp, wtimer_elapsed (bp->timer));
+  if (dltime == 0)
+    /* If the download was faster than the granularity of the timer,
+       fake some output so that we don't get the ugly "----.--" rate
+       at the download finish.  */
+    dltime = 1;
+
+  create_image (bp, dltime);
   display_image (bp->buffer);
 
   logputs (LOG_VERBOSE, "\n\n");
 
   xfree (bp->buffer);
-  wtimer_delete (bp->timer);
   xfree (bp);
 }
 
@@ -436,44 +481,54 @@ create_image (struct bar_progress *bp, long dltime)
   long size = bp->initial_length + bp->count;
 
   /* The progress bar should look like this:
-     xxx% |=======>             | xx KB/s nnnnn ETA: 00:00
+     xx% [=======>             ] nn.nnn rrK/s 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
+     "xx% " or "100%"  - percentage                - 4 chars exactly
+     "[]"              - progress bar decorations  - 2 chars exactly
+     " n,nnn,nnn,nnn"  - downloaded bytes          - 14 or less chars
+     " 1012.56K/s"     - dl rate                   - 11 chars exactly
+     " ETA xx:xx:xx"   - ETA                       - 13 or less chars
 
-     "=====>..."     - progress bar content      - the rest
+     "=====>..."       - progress bar content      - the rest
   */
-  int progress_len = screen_width - (5 + 3 + 12 + 11 + 13);
+  int progress_size = screen_width - (4 + 2 + 14 + 11 + 13);
 
-  if (progress_len < 7)
-    progress_len = 0;
+  if (progress_size < 5)
+    progress_size = 0;
 
-  /* "xxx% " */
+  /* "xx% " */
   if (bp->total_length > 0)
     {
       int percentage = (int)(100.0 * size / bp->total_length);
 
       assert (percentage <= 100);
 
-      sprintf (p, "%3d%% ", percentage);
-      p += 5;
+      if (percentage < 100)
+       sprintf (p, "%2d%% ", percentage);
+      else
+       strcpy (p, "100%");
+      p += 4;
+    }
+  else
+    {
+      *p++ = ' ';
+      *p++ = ' ';
+      *p++ = ' ';
+      *p++ = ' ';
     }
 
-  /* The progress bar: "|====>      | " */
-  if (progress_len && bp->total_length > 0)
+  /* The progress bar: "[====>      ]" */
+  if (progress_size && bp->total_length > 0)
     {
       double fraction = (double)size / bp->total_length;
-      int dlsz = (int)(fraction * progress_len);
+      int dlsz = (int)(fraction * progress_size);
       char *begin;
 
-      assert (dlsz <= progress_len);
+      assert (dlsz <= progress_size);
 
-      *p++ = '|';
+      *p++ = '[';
       begin = p;
 
       if (dlsz > 0)
@@ -484,32 +539,62 @@ create_image (struct bar_progress *bp, long dltime)
          *p++ = '>';
        }
 
-      while (p - begin < progress_len)
+      while (p - begin < progress_size)
        *p++ = ' ';
 
-      *p++ = '|';
-      *p++ = ' ';
+      *p++ = ']';
     }
+  else if (progress_size)
+    {
+      /* If we can't draw a real progress bar, then at least show
+        *something* to the user.  */
+      int ind = bp->tick % (progress_size * 2 - 6);
+      int i, pos;
+
+      /* Make the star move in two directions. */
+      if (ind < progress_size - 2)
+       pos = ind + 1;
+      else
+       pos = progress_size - (ind - progress_size + 5);
 
-  /* "2.3 KB/s " */
+      *p++ = '[';
+      for (i = 0; i < progress_size; i++)
+       {
+         if      (i == pos - 1) *p++ = '<';
+         else if (i == pos    ) *p++ = '=';
+         else if (i == pos + 1) *p++ = '>';
+         else
+           *p++ = ' ';
+       }
+      *p++ = ']';
+
+      ++bp->tick;
+    }
+
+  /* " 1,234,567" */
+  /* If there are 7 or less digits (9 because of "legible" comas),
+     print the number in constant space.  This will prevent the rest
+     of the line jerking at the beginning of download, but without
+     assigning maximum width in all cases.  */
+  sprintf (p, " %9s", legible (size));
+  p += strlen (p);
+
+  /* " 1012.45K/s" */
   if (dltime && bp->count)
     {
-      char *rt = rate (bp->count, dltime, 1);
-      strcpy (p, rt);
+      static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
+      int units = 0;
+      double dlrate = calc_rate (bp->count, dltime, &units);
+      sprintf (p, " %7.2f%s", dlrate, short_units[units]);
       p += strlen (p);
-      *p++ = ' ';
     }
   else
     {
-      strcpy (p, "----.-- KB/s ");
-      p += 13;
+      strcpy (p, "   --.--K/s");
+      p += 11;
     }
 
-  /* "12376 " */
-  sprintf (p, _("%ld "), size);
-  p += strlen (p);
-
-  /* "ETA: xx:xx:xx" */
+  /* " ETA xx:xx:xx" */
   if (bp->total_length > 0 && bp->count > 0)
     {
       int eta, eta_hrs, eta_min, eta_sec;
@@ -525,10 +610,10 @@ create_image (struct bar_progress *bp, long dltime)
       /*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++ = ' ';
       *p++ = 'E';
       *p++ = 'T';
       *p++ = 'A';
-      *p++ = ':';
       *p++ = ' ';
 
       if (eta_hrs > 99)
@@ -542,7 +627,7 @@ create_image (struct bar_progress *bp, long dltime)
     }
   else if (bp->total_length > 0)
     {
-      strcpy (p, "ETA: --:--");
+      strcpy (p, " ETA --:--");
       p += 10;
     }
 
@@ -553,24 +638,44 @@ create_image (struct bar_progress *bp, long dltime)
   *p = '\0';
 }
 
+/* Print the contents of the buffer as a one-line ASCII "image" so
+   that it can be overwritten next time.  */
+
 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';
-
+  char *del_buf = alloca (screen_width + 1);
+  memset (del_buf, '\b', screen_width);
+  del_buf[screen_width] = '\0';
   logputs (LOG_VERBOSE, del_buf);
+  logputs (LOG_VERBOSE, buf);
 }
 
 static void
-bar_set_params (const char *ignored)
+bar_set_params (const char *params)
 {
-  int sw = determine_screen_width ();
+  int sw;
+
+  if ((opt.lfilename
+#ifdef HAVE_ISATTY
+       || !isatty (fileno (stderr))
+#else
+       1
+#endif
+       )
+      && !(params != NULL
+          && 0 == strcmp (params, "force")))
+    {
+      /* We're not printing to a TTY, so revert to the fallback
+        display.  #### We're recursively calling
+        set_progress_implementation here, which is slightly kludgy.
+        It would be nicer if we provided that function a return value
+        indicating a failure of some sort.  */
+      set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
+      return;
+    }
+
+  sw = determine_screen_width ();
   if (sw && sw >= MINIMUM_SCREEN_WIDTH)
     screen_width = sw;
 }