]> sjero.net Git - wget/blobdiff - src/progress.c
[svn] Fixes by Christian Biere:
[wget] / src / progress.c
index a0689f152ab3676ed8bfc1e5a7620df905512f09..3fae615dcae86f0d8ba2d2af28941c97cf3bbc5b 100644 (file)
@@ -50,7 +50,8 @@ so, delete this exception statement from your version.  */
 #include "retr.h"
 
 struct progress_implementation {
-  char *name;
+  const char *name;
+  int interactive;
   void *(*create) PARAMS ((long, long));
   void (*update) PARAMS ((void *, long, double));
   void (*finish) PARAMS ((void *, double));
@@ -70,8 +71,8 @@ static void bar_finish PARAMS ((void *, double));
 static void bar_set_params PARAMS ((const char *));
 
 static struct progress_implementation implementations[] = {
-  { "dot", dot_create, dot_update, dot_finish, dot_set_params },
-  { "bar", bar_create, bar_update, bar_finish, bar_set_params }
+  { "dot", 0, dot_create, dot_update, dot_finish, dot_set_params },
+  { "bar", 1, bar_create, bar_update, bar_finish, bar_set_params }
 };
 static struct progress_implementation *current_impl;
 static int current_impl_locked;
@@ -168,6 +169,17 @@ progress_create (long initial, long total)
   return current_impl->create (initial, total);
 }
 
+/* Return non-zero if the progress gauge is "interactive", i.e. if it
+   can profit from being called regularly even in absence of data.
+   The progress bar is interactive because it regularly updates the
+   ETA and current update.  */
+
+int
+progress_interactive_p (void *progress)
+{
+  return current_impl->interactive;
+}
+
 /* Inform the progress gauge of newly received bytes.  DLTIME is the
    time in milliseconds since the beginning of the download.  */
 
@@ -206,10 +218,7 @@ struct dot_progress {
 static void *
 dot_create (long initial, long total)
 {
-  struct dot_progress *dp = xmalloc (sizeof (struct dot_progress));
-
-  memset (dp, 0, sizeof (*dp));
-
+  struct dot_progress *dp = xnew0 (struct dot_progress);
   dp->initial_length = initial;
   dp->total_length   = total;
 
@@ -411,7 +420,13 @@ dot_set_params (const char *params)
    create_image will overflow the buffer.  */
 #define MINIMUM_SCREEN_WIDTH 45
 
-static int screen_width = DEFAULT_SCREEN_WIDTH;
+/* The last known screen width.  This can be updated by the code that
+   detects that SIGWINCH was received (but it's never updated from the
+   signal handler).  */
+static int screen_width;
+
+/* A flag that, when set, means SIGWINCH was received.  */
+static volatile sig_atomic_t received_sigwinch;
 
 /* Size of the download speed history ring. */
 #define DLSPEED_HISTORY_SIZE 20
@@ -422,6 +437,11 @@ static int screen_width = DEFAULT_SCREEN_WIDTH;
    past.  */
 #define DLSPEED_SAMPLE_MIN 150
 
+/* The time after which the download starts to be considered
+   "stalled", i.e. the current bandwidth is not printed and the recent
+   download speeds are scratched.  */
+#define STALL_START_TIME 5000
+
 struct bar_progress {
   long initial_length;         /* how many bytes have been downloaded
                                   previously. */
@@ -463,8 +483,12 @@ struct bar_progress {
                                   position. */
   long recent_bytes;           /* bytes downloaded so far. */
 
+  int stalled;                 /* set when no data arrives for longer
+                                  than STALL_START_TIME, then reset
+                                  when new data arrives. */
+
   /* create_image() uses these to make sure that ETA information
-     doesn't flash. */
+     doesn't flicker. */
   double last_eta_time;                /* time of the last update to download
                                   speed and ETA, measured since the
                                   beginning of download. */
@@ -477,9 +501,7 @@ 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));
+  struct bar_progress *bp = xnew0 (struct bar_progress);
 
   /* In theory, our callers should take care of this pathological
      case, but it can sometimes happen. */
@@ -489,6 +511,18 @@ bar_create (long initial, long total)
   bp->initial_length = initial;
   bp->total_length   = total;
 
+  /* Initialize screen_width if this hasn't been done or if it might
+     have changed, as indicated by receiving SIGWINCH.  */
+  if (!screen_width || received_sigwinch)
+    {
+      screen_width = determine_screen_width ();
+      if (!screen_width)
+       screen_width = DEFAULT_SCREEN_WIDTH;
+      else if (screen_width < MINIMUM_SCREEN_WIDTH)
+       screen_width = MINIMUM_SCREEN_WIDTH;
+      received_sigwinch = 0;
+    }
+
   /* - 1 because we don't want to use the last screen column. */
   bp->width = screen_width - 1;
   /* + 1 for the terminating zero. */
@@ -522,11 +556,23 @@ bar_update (void *progress, long howmuch, double dltime)
 
   update_speed_ring (bp, howmuch, dltime);
 
-  if (screen_width - 1 != bp->width)
+  /* If SIGWINCH (the window size change signal) been received,
+     determine the new screen size and update the screen.  */
+  if (received_sigwinch)
     {
-      bp->width = screen_width - 1;
-      bp->buffer = xrealloc (bp->buffer, bp->width + 1);
-      force_screen_update = 1;
+      int old_width = screen_width;
+      screen_width = determine_screen_width ();
+      if (!screen_width)
+       screen_width = DEFAULT_SCREEN_WIDTH;
+      else if (screen_width < MINIMUM_SCREEN_WIDTH)
+       screen_width = MINIMUM_SCREEN_WIDTH;
+      if (screen_width != old_width)
+       {
+         bp->width = screen_width - 1;
+         bp->buffer = xrealloc (bp->buffer, bp->width + 1);
+         force_screen_update = 1;
+       }
+      received_sigwinch = 0;
     }
 
   if (dltime - bp->last_screen_update < 200 && !force_screen_update)
@@ -590,6 +636,38 @@ update_speed_ring (struct bar_progress *bp, long howmuch, double dltime)
   if (recent_age < DLSPEED_SAMPLE_MIN)
     return;
 
+  if (howmuch == 0)
+    {
+      /* If we're not downloading anything, we might be stalling,
+        i.e. not downloading anything for an extended period of time.
+        Since 0-reads do not enter the history ring, recent_age
+        effectively measures the time since last read.  */
+      if (recent_age >= STALL_START_TIME)
+       {
+         /* If we're stalling, reset the ring contents because it's
+            stale and because it will make bar_update stop printing
+            the (bogus) current bandwidth.  */
+         bp->stalled = 1;
+         xzero (*hist);
+         bp->recent_bytes = 0;
+       }
+      return;
+    }
+
+  /* We now have a non-zero amount of to store to the speed ring.  */
+
+  /* If the stall status was acquired, reset it. */
+  if (bp->stalled)
+    {
+      bp->stalled = 0;
+      /* "recent_age" includes the the entired stalled period, which
+        could be very long.  Don't update the speed ring with that
+        value because the current bandwidth would start too small.
+        Start with an arbitrary (but more reasonable) time value and
+        let it level out.  */
+      recent_age = 1000;
+    }
+
   /* Store "recent" bytes and download time to history ring at the
      position POS.  */
 
@@ -688,7 +766,7 @@ create_image (struct bar_progress *bp, double dl_total_time)
   else
     APPEND_LITERAL ("    ");
 
-  /* The progress bar: "[====>      ]" or "[--==>      ]". */
+  /* The progress bar: "[====>      ]" or "[++==>      ]". */
   if (progress_size && bp->total_length > 0)
     {
       /* Size of the initial portion. */
@@ -706,10 +784,10 @@ create_image (struct bar_progress *bp, double dl_total_time)
       *p++ = '[';
       begin = p;
 
-      /* Print the initial portion of the download with '-' chars, the
+      /* Print the initial portion of the download with '+' chars, the
         rest with '=' and one '>'.  */
       for (i = 0; i < insz; i++)
-       *p++ = '-';
+       *p++ = '+';
       dlsz -= insz;
       if (dlsz > 0)
        {
@@ -756,7 +834,7 @@ create_image (struct bar_progress *bp, double dl_total_time)
   /* " 1012.45K/s" */
   if (hist->total_time && hist->total_bytes)
     {
-      static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
+      static const char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
       int units = 0;
       /* Calculate the download speed using the history ring and
         recent data that hasn't made it to the ring yet.  */
@@ -772,7 +850,7 @@ create_image (struct bar_progress *bp, double dl_total_time)
   /* " ETA xx:xx:xx"; wait for three seconds before displaying the ETA.
      That's because the ETA value needs a while to become
      reliable.  */
-  if (bp->total_length > 0 && dl_total_time > 3000)
+  if (bp->total_length > 0 && bp->count > 0 && dl_total_time > 3000)
     {
       long eta;
       int eta_hrs, eta_min, eta_sec;
@@ -780,8 +858,9 @@ create_image (struct bar_progress *bp, double dl_total_time)
       /* Don't change the value of ETA more than approximately once
         per second; doing so would cause flashing without providing
         any value to the user. */
-      if (dl_total_time - bp->last_eta_time < 900
-         && bp->last_eta_value != 0)
+      if (bp->total_length != size
+         && bp->last_eta_value != 0
+         && dl_total_time - bp->last_eta_time < 900)
        eta = bp->last_eta_value;
       else
        {
@@ -848,7 +927,6 @@ display_image (char *buf)
 static void
 bar_set_params (const char *params)
 {
-  int sw;
   char *term = getenv ("TERM");
 
   if (params
@@ -861,8 +939,6 @@ bar_set_params (const char *params)
          TTY -- when logging to file, it is better to review the
          dots.  */
        || !isatty (fileno (stderr))
-#else
-       1
 #endif
        /* Normally we don't depend on terminal type because the
          progress bar only uses ^M to move the cursor to the
@@ -881,19 +957,13 @@ bar_set_params (const char *params)
       set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
       return;
     }
-
-  sw = determine_screen_width ();
-  if (sw && sw >= MINIMUM_SCREEN_WIDTH)
-    screen_width = sw;
 }
 
 #ifdef SIGWINCH
 RETSIGTYPE
 progress_handle_sigwinch (int sig)
 {
-  int sw = determine_screen_width ();
-  if (sw && sw >= MINIMUM_SCREEN_WIDTH)
-    screen_width = sw;
+  received_sigwinch = 1;
   signal (SIGWINCH, progress_handle_sigwinch);
 }
 #endif