]> sjero.net Git - wget/blobdiff - src/progress.c
[svn] New ETA display.
[wget] / src / progress.c
index 2c6b5820782764f2d80883b8a0e73602ba9feaed..137794d6cb4c3047dc6933f210390b5f517cd154 100644 (file)
@@ -31,18 +31,12 @@ so, delete this exception statement from your version.  */
 
 #include <stdio.h>
 #include <stdlib.h>
 
 #include <stdio.h>
 #include <stdlib.h>
-#ifdef HAVE_STRING_H
-# include <string.h>
-#else
-# include <strings.h>
-#endif /* HAVE_STRING_H */
+#include <string.h>
 #include <assert.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
 #include <assert.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
-#ifdef HAVE_SIGNAL_H
-# include <signal.h>
-#endif
+#include <signal.h>
 
 #include "wget.h"
 #include "progress.h"
 
 #include "wget.h"
 #include "progress.h"
@@ -51,24 +45,24 @@ so, delete this exception statement from your version.  */
 
 struct progress_implementation {
   const char *name;
 
 struct progress_implementation {
   const char *name;
-  int interactive;
-  void *(*create) PARAMS ((wgint, wgint));
-  void (*update) PARAMS ((void *, wgint, double));
-  void (*finish) PARAMS ((void *, double));
-  void (*set_params) PARAMS ((const char *));
+  bool interactive;
+  void *(*create) (wgint, wgint);
+  void (*update) (void *, wgint, double);
+  void (*finish) (void *, double);
+  void (*set_params) (const char *);
 };
 
 /* Necessary forward declarations. */
 
 };
 
 /* Necessary forward declarations. */
 
-static void *dot_create PARAMS ((wgint, wgint));
-static void dot_update PARAMS ((void *, wgint, double));
-static void dot_finish PARAMS ((void *, double));
-static void dot_set_params PARAMS ((const char *));
+static void *dot_create (wgint, wgint);
+static void dot_update (void *, wgint, double);
+static void dot_finish (void *, double);
+static void dot_set_params (const char *);
 
 
-static void *bar_create PARAMS ((wgint, wgint));
-static void bar_update PARAMS ((void *, wgint, double));
-static void bar_finish PARAMS ((void *, double));
-static void bar_set_params PARAMS ((const char *));
+static void *bar_create (wgint, wgint);
+static void bar_update (void *, wgint, double);
+static void bar_finish (void *, double);
+static void bar_set_params (const char *);
 
 static struct progress_implementation implementations[] = {
   { "dot", 0, dot_create, dot_update, dot_finish, dot_set_params },
 
 static struct progress_implementation implementations[] = {
   { "dot", 0, dot_create, dot_update, dot_finish, dot_set_params },
@@ -90,10 +84,10 @@ static int current_impl_locked;
 
 #define FALLBACK_PROGRESS_IMPLEMENTATION "dot"
 
 
 #define FALLBACK_PROGRESS_IMPLEMENTATION "dot"
 
-/* Return non-zero if NAME names a valid progress bar implementation.
-   The characters after the first : will be ignored.  */
+/* Return true if NAME names a valid progress bar implementation.  The
+   characters after the first : will be ignored.  */
 
 
-int
+bool
 valid_progress_implementation_p (const char *name)
 {
   int i;
 valid_progress_implementation_p (const char *name)
 {
   int i;
@@ -103,8 +97,8 @@ valid_progress_implementation_p (const char *name)
 
   for (i = 0; i < countof (implementations); i++, pi++)
     if (!strncmp (pi->name, name, namelen))
 
   for (i = 0; i < countof (implementations); i++, pi++)
     if (!strncmp (pi->name, name, namelen))
-      return 1;
-  return 0;
+      return true;
+  return false;
 }
 
 /* Set the progress implementation to NAME.  */
 }
 
 /* Set the progress implementation to NAME.  */
@@ -169,12 +163,12 @@ progress_create (wgint initial, wgint total)
   return current_impl->create (initial, 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.  */
+/* Return true 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
+bool
 progress_interactive_p (void *progress)
 {
   return current_impl->interactive;
 progress_interactive_p (void *progress)
 {
   return current_impl->interactive;
@@ -285,7 +279,7 @@ dot_update (void *progress, wgint howmuch, double dltime)
   int dot_bytes = opt.dot_bytes;
   wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
 
   int dot_bytes = opt.dot_bytes;
   wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
 
-  log_set_flush (0);
+  log_set_flush (false);
 
   dp->accumulated += howmuch;
   for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
 
   dp->accumulated += howmuch;
   for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
@@ -313,7 +307,7 @@ dot_update (void *progress, wgint howmuch, double dltime)
        }
     }
 
        }
     }
 
-  log_set_flush (1);
+  log_set_flush (true);
 }
 
 /* Dot-progress backend for progress_finish. */
 }
 
 /* Dot-progress backend for progress_finish. */
@@ -326,7 +320,7 @@ dot_finish (void *progress, double dltime)
   wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
   int i;
 
   wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
   int i;
 
-  log_set_flush (0);
+  log_set_flush (false);
 
   if (dp->dots == 0)
     logprintf (LOG_VERBOSE, "\n%5ldK", (long) (dp->rows * row_bytes / 1024));
 
   if (dp->dots == 0)
     logprintf (LOG_VERBOSE, "\n%5ldK", (long) (dp->rows * row_bytes / 1024));
@@ -352,7 +346,7 @@ dot_finish (void *progress, double dltime)
   }
 
   logputs (LOG_VERBOSE, "\n\n");
   }
 
   logputs (LOG_VERBOSE, "\n\n");
-  log_set_flush (0);
+  log_set_flush (false);
 
   xfree (dp);
 }
 
   xfree (dp);
 }
@@ -483,7 +477,7 @@ struct bar_progress {
                                   position. */
   wgint recent_bytes;          /* bytes downloaded so far. */
 
                                   position. */
   wgint recent_bytes;          /* bytes downloaded so far. */
 
-  int stalled;                 /* set when no data arrives for longer
+  bool stalled;                        /* set when no data arrives for longer
                                   than STALL_START_TIME, then reset
                                   when new data arrives. */
 
                                   than STALL_START_TIME, then reset
                                   when new data arrives. */
 
@@ -492,11 +486,11 @@ struct bar_progress {
   double last_eta_time;                /* time of the last update to download
                                   speed and ETA, measured since the
                                   beginning of download. */
   double last_eta_time;                /* time of the last update to download
                                   speed and ETA, measured since the
                                   beginning of download. */
-  wgint last_eta_value;
+  int last_eta_value;
 };
 
 };
 
-static void create_image PARAMS ((struct bar_progress *, double));
-static void display_image PARAMS ((char *));
+static void create_image (struct bar_progress *, double);
+static void display_image (char *);
 
 static void *
 bar_create (wgint initial, wgint total)
 
 static void *
 bar_create (wgint initial, wgint total)
@@ -536,13 +530,13 @@ bar_create (wgint initial, wgint total)
   return bp;
 }
 
   return bp;
 }
 
-static void update_speed_ring PARAMS ((struct bar_progress *, wgint, double));
+static void update_speed_ring (struct bar_progress *, wgint, double);
 
 static void
 bar_update (void *progress, wgint howmuch, double dltime)
 {
   struct bar_progress *bp = progress;
 
 static void
 bar_update (void *progress, wgint howmuch, double dltime)
 {
   struct bar_progress *bp = progress;
-  int force_screen_update = 0;
+  bool force_screen_update = false;
 
   bp->count += howmuch;
   if (bp->total_length > 0
 
   bp->count += howmuch;
   if (bp->total_length > 0
@@ -570,7 +564,7 @@ bar_update (void *progress, wgint howmuch, double dltime)
        {
          bp->width = screen_width - 1;
          bp->buffer = xrealloc (bp->buffer, bp->width + 1);
        {
          bp->width = screen_width - 1;
          bp->buffer = xrealloc (bp->buffer, bp->width + 1);
-         force_screen_update = 1;
+         force_screen_update = true;
        }
       received_sigwinch = 0;
     }
        }
       received_sigwinch = 0;
     }
@@ -647,7 +641,7 @@ update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime)
          /* 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.  */
          /* 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;
+         bp->stalled = true;
          xzero (*hist);
          bp->recent_bytes = 0;
        }
          xzero (*hist);
          bp->recent_bytes = 0;
        }
@@ -659,7 +653,7 @@ update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime)
   /* If the stall status was acquired, reset it. */
   if (bp->stalled)
     {
   /* If the stall status was acquired, reset it. */
   if (bp->stalled)
     {
-      bp->stalled = 0;
+      bp->stalled = false;
       /* "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.
       /* "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.
@@ -706,6 +700,8 @@ update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime)
 #endif
 }
 
 #endif
 }
 
+static const char *eta_to_human (int);
+
 #define APPEND_LITERAL(s) do {                 \
   memcpy (p, s, sizeof (s) - 1);               \
   p += sizeof (s) - 1;                         \
 #define APPEND_LITERAL(s) do {                 \
   memcpy (p, s, sizeof (s) - 1);               \
   p += sizeof (s) - 1;                         \
@@ -727,7 +723,7 @@ create_image (struct bar_progress *bp, double dl_total_time)
   struct bar_progress_hist *hist = &bp->hist;
 
   /* The progress bar should look like this:
   struct bar_progress_hist *hist = &bp->hist;
 
   /* The progress bar should look like this:
-     xx% [=======>             ] nn,nnn 12.34K/s ETA 00:00
+     xx% [=======>             ] nn,nnn 12.34K/s  eta 36m 51s
 
      Calculate the geometry.  The idea is to assign as much room as
      possible to the progress bar.  The other idea is to never let
 
      Calculate the geometry.  The idea is to assign as much room as
      possible to the progress bar.  The other idea is to never let
@@ -740,7 +736,7 @@ create_image (struct bar_progress *bp, double dl_total_time)
      "[]"              - progress bar decorations - 2 chars
      " nnn,nnn,nnn"    - downloaded bytes         - 12 chars or very rarely more
      " 1012.56K/s"     - dl rate                  - 11 chars
      "[]"              - progress bar decorations - 2 chars
      " nnn,nnn,nnn"    - downloaded bytes         - 12 chars or very rarely more
      " 1012.56K/s"     - dl rate                  - 11 chars
-     " ETA xx:xx:xx"   - ETA                      - 13 chars
+     "  eta 36m 51s"   - ETA                      - 13 chars
 
      "=====>..."       - progress bar             - the rest
   */
 
      "=====>..."       - progress bar             - the rest
   */
@@ -754,7 +750,6 @@ create_image (struct bar_progress *bp, double dl_total_time)
   if (bp->total_length > 0)
     {
       int percentage = (int)(100.0 * size / bp->total_length);
   if (bp->total_length > 0)
     {
       int percentage = (int)(100.0 * size / bp->total_length);
-
       assert (percentage <= 100);
 
       if (percentage < 100)
       assert (percentage <= 100);
 
       if (percentage < 100)
@@ -847,13 +842,12 @@ create_image (struct bar_progress *bp, double dl_total_time)
   else
     APPEND_LITERAL ("   --.--K/s");
 
   else
     APPEND_LITERAL ("   --.--K/s");
 
-  /* " ETA xx:xx:xx"; wait for three seconds before displaying the ETA.
+  /* "  ETA ..m ..s"; 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 && bp->count > 0 && dl_total_time > 3000)
     {
      That's because the ETA value needs a while to become
      reliable.  */
   if (bp->total_length > 0 && bp->count > 0 && dl_total_time > 3000)
     {
-      wgint eta;
-      int eta_hrs, eta_min, eta_sec;
+      int eta;
 
       /* Don't change the value of ETA more than approximately once
         per second; doing so would cause flashing without providing
 
       /* Don't change the value of ETA more than approximately once
         per second; doing so would cause flashing without providing
@@ -870,38 +864,18 @@ create_image (struct bar_progress *bp, double dl_total_time)
             hist->total_time and bp->count with hist->total_bytes.
             I found that doing that results in a very jerky and
             ultimately unreliable ETA.  */
             hist->total_time and bp->count with hist->total_bytes.
             I found that doing that results in a very jerky and
             ultimately unreliable ETA.  */
-         double time_sofar = (double)dl_total_time / 1000;
+         double time_sofar = (double) dl_total_time / 1000;
          wgint bytes_remaining = bp->total_length - size;
          wgint bytes_remaining = bp->total_length - size;
-         eta = (wgint) (time_sofar * bytes_remaining / bp->count);
+         eta = (int) (time_sofar * bytes_remaining / bp->count + 0.5);
          bp->last_eta_value = eta;
          bp->last_eta_time = dl_total_time;
        }
 
          bp->last_eta_value = eta;
          bp->last_eta_time = dl_total_time;
        }
 
-      eta_hrs = eta / 3600, eta %= 3600;
-      eta_min = eta / 60,   eta %= 60;
-      eta_sec = eta;
-
-      if (eta_hrs > 99)
-       goto no_eta;
-
-      if (eta_hrs == 0)
-       {
-         /* Hours not printed: pad with three spaces. */
-         APPEND_LITERAL ("   ");
-         sprintf (p, " ETA %02d:%02d", eta_min, eta_sec);
-       }
-      else
-       {
-         if (eta_hrs < 10)
-           /* Hours printed with one digit: pad with one space. */
-           *p++ = ' ';
-         sprintf (p, " ETA %d:%02d:%02d", eta_hrs, eta_min, eta_sec);
-       }
+      sprintf (p, "  eta %s", eta_to_human (eta));
       p += strlen (p);
     }
   else if (bp->total_length > 0)
     {
       p += strlen (p);
     }
   else if (bp->total_length > 0)
     {
-    no_eta:
       APPEND_LITERAL ("             ");
     }
 
       APPEND_LITERAL ("             ");
     }
 
@@ -918,7 +892,7 @@ create_image (struct bar_progress *bp, double dl_total_time)
 static void
 display_image (char *buf)
 {
 static void
 display_image (char *buf)
 {
-  int old = log_set_save_context (0);
+  bool old = log_set_save_context (false);
   logputs (LOG_VERBOSE, "\r");
   logputs (LOG_VERBOSE, buf);
   log_set_save_context (old);
   logputs (LOG_VERBOSE, "\r");
   logputs (LOG_VERBOSE, buf);
   log_set_save_context (old);
@@ -960,10 +934,41 @@ bar_set_params (const char *params)
 }
 
 #ifdef SIGWINCH
 }
 
 #ifdef SIGWINCH
-RETSIGTYPE
+void
 progress_handle_sigwinch (int sig)
 {
   received_sigwinch = 1;
   signal (SIGWINCH, progress_handle_sigwinch);
 }
 #endif
 progress_handle_sigwinch (int sig)
 {
   received_sigwinch = 1;
   signal (SIGWINCH, progress_handle_sigwinch);
 }
 #endif
+
+/* Provide a human-readable rendition of the ETA.  It never occupies
+   more than 7 characters of screen space.  */
+
+static const char *
+eta_to_human (int secs)
+{
+  static char buf[10];         /* 8 is enough, but just in case */
+  static int last = -1;
+
+  /* Trivial optimization.  This function can be called every 200
+     msecs (see bar_update) for fast downloads, but ETA will only
+     change once per 900 msecs (see create_image).  */
+  if (secs == last)
+    return buf;
+  last = secs;
+
+  if (secs < 100)
+    sprintf (buf, "%ds", secs);
+  else if (secs < 100 * 60)
+    sprintf (buf, "%dm %ds", secs / 60, secs % 60);
+  else if (secs < 100 * 3600)
+    sprintf (buf, "%dh %dm", secs / 3600, (secs / 60) % 60);
+  else if (secs < 100 * 86400)
+    sprintf (buf, "%dd %dh", secs / 86400, (secs / 3600) % 60);
+  else
+    /* (2^31-1)/86400 doesn't overflow BUF. */
+    sprintf (buf, "%dd", secs / 86400);
+
+  return buf;
+}