]> sjero.net Git - wget/blobdiff - src/progress.c
[svn] New ETA display.
[wget] / src / progress.c
index 33ccb7c9c465e358fc691ca50ee9f5c7753ae6de..137794d6cb4c3047dc6933f210390b5f517cd154 100644 (file)
@@ -31,18 +31,12 @@ so, delete this exception statement from your version.  */
 
 #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
-#ifdef HAVE_SIGNAL_H
-# include <signal.h>
-#endif
+#include <signal.h>
 
 #include "wget.h"
 #include "progress.h"
@@ -50,25 +44,25 @@ so, delete this exception statement from your version.  */
 #include "retr.h"
 
 struct progress_implementation {
-  char *name;
-  int interactive;
-  void *(*create) PARAMS ((long, long));
-  void (*update) PARAMS ((void *, long, double));
-  void (*finish) PARAMS ((void *, double));
-  void (*set_params) PARAMS ((const char *));
+  const char *name;
+  bool interactive;
+  void *(*create) (wgint, wgint);
+  void (*update) (void *, wgint, double);
+  void (*finish) (void *, double);
+  void (*set_params) (const char *);
 };
 
 /* Necessary forward declarations. */
 
-static void *dot_create PARAMS ((long, long));
-static void dot_update PARAMS ((void *, long, 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 ((long, long));
-static void bar_update PARAMS ((void *, long, 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 },
@@ -90,21 +84,21 @@ static int current_impl_locked;
 
 #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 = 0;
+  int i;
   struct progress_implementation *pi = implementations;
   char *colon = strchr (name, ':');
   int namelen = colon ? colon - name : strlen (name);
 
   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.  */
@@ -156,7 +150,7 @@ progress_schedule_redirect (void)
    advance.  */
 
 void *
-progress_create (long initial, long total)
+progress_create (wgint initial, wgint total)
 {
   /* Check if the log status has changed under our feet. */
   if (output_redirected)
@@ -169,12 +163,12 @@ 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.  */
+/* 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;
@@ -184,7 +178,7 @@ progress_interactive_p (void *progress)
    time in milliseconds since the beginning of the download.  */
 
 void
-progress_update (void *progress, long howmuch, double dltime)
+progress_update (void *progress, wgint howmuch, double dltime)
 {
   current_impl->update (progress, howmuch, dltime);
 }
@@ -201,9 +195,9 @@ progress_finish (void *progress, double dltime)
 /* Dot-printing. */
 
 struct dot_progress {
-  long initial_length;         /* how many bytes have been downloaded
+  wgint initial_length;                /* how many bytes have been downloaded
                                   previously. */
-  long total_length;           /* expected total byte count when the
+  wgint total_length;          /* expected total byte count when the
                                   download finishes */
 
   int accumulated;
@@ -216,7 +210,7 @@ struct dot_progress {
 /* Dot-progress backend for progress_create. */
 
 static void *
-dot_create (long initial, long total)
+dot_create (wgint initial, wgint total)
 {
   struct dot_progress *dp = xnew0 (struct dot_progress);
   dp->initial_length = initial;
@@ -225,10 +219,10 @@ dot_create (long initial, long total)
   if (dp->initial_length)
     {
       int dot_bytes = opt.dot_bytes;
-      long row_bytes = opt.dot_bytes * opt.dots_in_line;
+      wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
 
       int remainder = (int) (dp->initial_length % row_bytes);
-      long skipped = dp->initial_length - remainder;
+      wgint skipped = dp->initial_length - remainder;
 
       if (skipped)
        {
@@ -244,7 +238,7 @@ dot_create (long initial, long total)
                     2 + skipped_k_len, "", skipped_k);
        }
 
-      logprintf (LOG_VERBOSE, "\n%5ldK", skipped / 1024);
+      logprintf (LOG_VERBOSE, "\n%5ldK", (long) (skipped / 1024));
       for (; remainder >= dot_bytes; remainder -= dot_bytes)
        {
          if (dp->dots % opt.dot_spacing == 0)
@@ -262,14 +256,14 @@ dot_create (long initial, long total)
 }
 
 static void
-print_percentage (long bytes, long expected)
+print_percentage (wgint bytes, wgint expected)
 {
   int percentage = (int)(100.0 * bytes / expected);
   logprintf (LOG_VERBOSE, "%3d%%", percentage);
 }
 
 static void
-print_download_speed (struct dot_progress *dp, long bytes, double dltime)
+print_download_speed (struct dot_progress *dp, wgint bytes, double dltime)
 {
   logprintf (LOG_VERBOSE, " %s",
             retr_rate (bytes, dltime - dp->last_timer_value, 1));
@@ -279,19 +273,19 @@ print_download_speed (struct dot_progress *dp, long bytes, double dltime)
 /* Dot-progress backend for progress_update. */
 
 static void
-dot_update (void *progress, long howmuch, double dltime)
+dot_update (void *progress, wgint howmuch, double dltime)
 {
   struct dot_progress *dp = progress;
   int dot_bytes = opt.dot_bytes;
-  long row_bytes = opt.dot_bytes * opt.dots_in_line;
+  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)
     {
       if (dp->dots == 0)
-       logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
+       logprintf (LOG_VERBOSE, "\n%5ldK", (long) (dp->rows * row_bytes / 1024));
 
       if (dp->dots % opt.dot_spacing == 0)
        logputs (LOG_VERBOSE, " ");
@@ -300,7 +294,7 @@ dot_update (void *progress, long howmuch, double dltime)
       ++dp->dots;
       if (dp->dots >= opt.dots_in_line)
        {
-         long row_qty = row_bytes;
+         wgint row_qty = row_bytes;
          if (dp->rows == dp->initial_length / row_bytes)
            row_qty -= dp->initial_length % row_bytes;
 
@@ -313,7 +307,7 @@ dot_update (void *progress, long howmuch, double dltime)
        }
     }
 
-  log_set_flush (1);
+  log_set_flush (true);
 }
 
 /* Dot-progress backend for progress_finish. */
@@ -323,13 +317,13 @@ dot_finish (void *progress, double dltime)
 {
   struct dot_progress *dp = progress;
   int dot_bytes = opt.dot_bytes;
-  long row_bytes = opt.dot_bytes * opt.dots_in_line;
+  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", dp->rows * row_bytes / 1024);
+    logprintf (LOG_VERBOSE, "\n%5ldK", (long) (dp->rows * row_bytes / 1024));
   for (i = dp->dots; i < opt.dots_in_line; i++)
     {
       if (i % opt.dot_spacing == 0)
@@ -345,14 +339,14 @@ dot_finish (void *progress, double dltime)
     }
 
   {
-    long row_qty = dp->dots * dot_bytes + dp->accumulated;
+    wgint 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);
+  log_set_flush (false);
 
   xfree (dp);
 }
@@ -443,11 +437,11 @@ static volatile sig_atomic_t received_sigwinch;
 #define STALL_START_TIME 5000
 
 struct bar_progress {
-  long initial_length;         /* how many bytes have been downloaded
+  wgint initial_length;                /* how many bytes have been downloaded
                                   previously. */
-  long total_length;           /* expected total byte count when the
+  wgint total_length;          /* expected total byte count when the
                                   download finishes */
-  long count;                  /* bytes downloaded so far */
+  wgint count;                 /* bytes downloaded so far */
 
   double last_screen_update;   /* time of the last screen update,
                                   measured since the beginning of
@@ -470,20 +464,20 @@ struct bar_progress {
      details.  */
   struct bar_progress_hist {
     int pos;
-    long times[DLSPEED_HISTORY_SIZE];
-    long bytes[DLSPEED_HISTORY_SIZE];
+    wgint times[DLSPEED_HISTORY_SIZE];
+    wgint bytes[DLSPEED_HISTORY_SIZE];
 
     /* The sum of times and bytes respectively, maintained for
        efficiency. */
-    long total_time;
-    long total_bytes;
+    wgint total_time;
+    wgint total_bytes;
   } hist;
 
   double recent_start;         /* timestamp of beginning of current
                                   position. */
-  long recent_bytes;           /* bytes downloaded so far. */
+  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. */
 
@@ -492,14 +486,14 @@ struct bar_progress {
   double last_eta_time;                /* time of the last update to download
                                   speed and ETA, measured since the
                                   beginning of download. */
-  long 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 (long initial, long total)
+bar_create (wgint initial, wgint total)
 {
   struct bar_progress *bp = xnew0 (struct bar_progress);
 
@@ -536,13 +530,13 @@ bar_create (long initial, long total)
   return bp;
 }
 
-static void update_speed_ring PARAMS ((struct bar_progress *, long, double));
+static void update_speed_ring (struct bar_progress *, wgint, double);
 
 static void
-bar_update (void *progress, long howmuch, double dltime)
+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
@@ -570,7 +564,7 @@ bar_update (void *progress, long howmuch, double dltime)
        {
          bp->width = screen_width - 1;
          bp->buffer = xrealloc (bp->buffer, bp->width + 1);
-         force_screen_update = 1;
+         force_screen_update = true;
        }
       received_sigwinch = 0;
     }
@@ -622,7 +616,7 @@ bar_finish (void *progress, double dltime)
    3-second average would be too erratic.  */
 
 static void
-update_speed_ring (struct bar_progress *bp, long howmuch, double dltime)
+update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime)
 {
   struct bar_progress_hist *hist = &bp->hist;
   double recent_age = dltime - bp->recent_start;
@@ -647,7 +641,7 @@ update_speed_ring (struct bar_progress *bp, long 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.  */
-         bp->stalled = 1;
+         bp->stalled = true;
          xzero (*hist);
          bp->recent_bytes = 0;
        }
@@ -659,7 +653,7 @@ update_speed_ring (struct bar_progress *bp, long howmuch, double dltime)
   /* 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.
@@ -706,6 +700,8 @@ update_speed_ring (struct bar_progress *bp, long howmuch, double dltime)
 #endif
 }
 
+static const char *eta_to_human (int);
+
 #define APPEND_LITERAL(s) do {                 \
   memcpy (p, s, sizeof (s) - 1);               \
   p += sizeof (s) - 1;                         \
@@ -719,15 +715,15 @@ static void
 create_image (struct bar_progress *bp, double dl_total_time)
 {
   char *p = bp->buffer;
-  long size = bp->initial_length + bp->count;
+  wgint size = bp->initial_length + bp->count;
 
-  char *size_legible = legible (size);
+  char *size_legible = with_thousand_seps (size);
   int size_legible_len = strlen (size_legible);
 
   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
@@ -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
-     " ETA xx:xx:xx"   - ETA                      - 13 chars
+     "  eta 36m 51s"   - ETA                      - 13 chars
 
      "=====>..."       - 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);
-
       assert (percentage <= 100);
 
       if (percentage < 100)
@@ -828,17 +823,17 @@ create_image (struct bar_progress *bp, double dl_total_time)
     }
 
   /* " 234,567,890" */
-  sprintf (p, " %-11s", legible (size));
+  sprintf (p, " %-11s", with_thousand_seps (size));
   p += strlen (p);
 
   /* " 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.  */
-      long dlquant = hist->total_bytes + bp->recent_bytes;
+      wgint dlquant = hist->total_bytes + bp->recent_bytes;
       double dltime = hist->total_time + (dl_total_time - bp->recent_start);
       double dlspeed = calc_rate (dlquant, dltime, &units);
       sprintf (p, " %7.2f%s", dlspeed, short_units[units]);
@@ -847,13 +842,12 @@ create_image (struct bar_progress *bp, double dl_total_time)
   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)
     {
-      long 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
@@ -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.  */
-         double time_sofar = (double)dl_total_time / 1000;
-         long bytes_remaining = bp->total_length - size;
-         eta = (long) (time_sofar * bytes_remaining / bp->count);
+         double time_sofar = (double) dl_total_time / 1000;
+         wgint bytes_remaining = bp->total_length - size;
+         eta = (int) (time_sofar * bytes_remaining / bp->count + 0.5);
          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)
     {
-    no_eta:
       APPEND_LITERAL ("             ");
     }
 
@@ -918,7 +892,7 @@ create_image (struct bar_progress *bp, double dl_total_time)
 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);
@@ -939,8 +913,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
@@ -962,10 +934,41 @@ bar_set_params (const char *params)
 }
 
 #ifdef SIGWINCH
-RETSIGTYPE
+void
 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;
+}