]> sjero.net Git - wget/blobdiff - src/progress.c
[svn] Fall back to dot progress when forced to background.
[wget] / src / progress.c
index e167b7bfc41843c4288c9caf0f937fe2615e0fab..4bf8541b3bdb7157a2fede69e7395687b51f1890 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
@@ -39,21 +39,21 @@ 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 *dot_create PARAMS ((long, long));
-static void dot_update PARAMS ((void *, long));
-static void dot_finish PARAMS ((void *));
+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[] = {
@@ -61,14 +61,20 @@ static struct progress_implementation implementations[] = {
   { "bar", bar_create, bar_update, bar_finish, bar_set_params }
 };
 static struct progress_implementation *current_impl;
+int current_impl_locked;
 
-/* Default progress implementation should be something that works
+/* 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 DEFAULT_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.  */
@@ -106,6 +112,7 @@ set_progress_implementation (const char *name)
     if (!strncmp (pi->name, name, namelen))
       {
        current_impl = pi;
+       current_impl_locked = 0;
 
        if (colon)
          /* We call pi->set_params even if colon is NULL because we
@@ -120,6 +127,14 @@ set_progress_implementation (const char *name)
   abort ();
 }
 
+static int output_redirected;
+
+void
+progress_schedule_redirect (void)
+{
+  output_redirected = 1;
+}
+
 /* 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
@@ -129,24 +144,33 @@ set_progress_implementation (const char *name)
 void *
 progress_create (long initial, long total)
 {
+  /* Check if the log status has changed under our feet. */
+  if (output_redirected)
+    {
+      if (!current_impl_locked)
+       set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
+      output_redirected = 0;
+    }
+
   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. */
@@ -161,9 +185,6 @@ 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;
 };
 
@@ -178,7 +199,6 @@ dot_create (long initial, long total)
 
   dp->initial_length = initial;
   dp->total_length   = total;
-  dp->timer = wtimer_new ();
 
   if (dp->initial_length)
     {
@@ -190,9 +210,16 @@ dot_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);
@@ -220,18 +247,17 @@ print_percentage (long bytes, long expected)
 }
 
 static void
-print_download_speed (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;
+            retr_rate (bytes, dltime - dp->last_timer_value, 1));
+  dp->last_timer_value = dltime;
 }
 
 /* Dot-progress backend for progress_update. */
 
 static void
-dot_update (void *progress, long howmuch)
+dot_update (void *progress, long howmuch, long dltime)
 {
   struct dot_progress *dp = progress;
   int dot_bytes = opt.dot_bytes;
@@ -252,14 +278,16 @@ dot_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_download_speed (dp,
-                               row_bytes - (dp->initial_length % row_bytes));
+         print_download_speed (dp, row_qty, dltime);
        }
     }
 
@@ -269,7 +297,7 @@ dot_update (void *progress, long howmuch)
 /* Dot-progress backend for progress_finish. */
 
 static void
-dot_finish (void *progress)
+dot_finish (void *progress, long dltime)
 {
   struct dot_progress *dp = progress;
   int dot_bytes = opt.dot_bytes;
@@ -278,6 +306,8 @@ dot_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)
@@ -292,25 +322,30 @@ dot_finish (void *progress)
                        dp->total_length);
     }
 
-  print_download_speed (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
 dot_set_params (const char *params)
 {
+  if (!params || !*params)
+    params = opt.dot_style;
+
   if (!params)
     return;
 
@@ -372,14 +407,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));
@@ -394,7 +429,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);
 
@@ -407,11 +441,10 @@ 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
@@ -440,24 +473,22 @@ bar_update (void *progress, long howmuch)
 }
 
 static void
-bar_finish (void *progress)
+bar_finish (void *progress, long dltime)
 {
   struct bar_progress *bp = progress;
-  long elapsed = wtimer_elapsed (bp->timer);
 
-  if (elapsed == 0)
+  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.  */
-    elapsed = 1;
+    dltime = 1;
 
-  create_image (bp, elapsed);
+  create_image (bp, dltime);
   display_image (bp->buffer);
 
   logputs (LOG_VERBOSE, "\n\n");
 
   xfree (bp->buffer);
-  wtimer_delete (bp->timer);
   xfree (bp);
 }
 
@@ -468,44 +499,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  - 2 chars
-     "1012.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 + 2 + 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)
@@ -516,32 +557,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);
+
+      *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;
     }
 
-  /* "1012.45 K/s " */
+  /* " 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, "  --.-- K/s ");
-      p += 12;
+      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;
@@ -557,10 +628,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)
@@ -574,7 +645,7 @@ create_image (struct bar_progress *bp, long dltime)
     }
   else if (bp->total_length > 0)
     {
-      strcpy (p, "ETA: --:--");
+      strcpy (p, " ETA --:--");
       p += 10;
     }
 
@@ -591,15 +662,8 @@ create_image (struct bar_progress *bp, long dltime)
 static void
 display_image (char *buf)
 {
-  int len = strlen (buf);
-  char *del_buf = alloca (len + 1);
-
+  logputs (LOG_VERBOSE, "\r");
   logputs (LOG_VERBOSE, buf);
-
-  memset (del_buf, '\b', len);
-  del_buf[len] = '\0';
-
-  logputs (LOG_VERBOSE, del_buf);
 }
 
 static void
@@ -607,6 +671,10 @@ bar_set_params (const char *params)
 {
   int sw;
 
+  if (params
+      && 0 == strcmp (params, "force"))
+    current_impl_locked = 1;
+
   if ((opt.lfilename
 #ifdef HAVE_ISATTY
        || !isatty (fileno (stderr))
@@ -614,15 +682,14 @@ bar_set_params (const char *params)
        1
 #endif
        )
-      && !(params != NULL
-          && 0 == strcmp (params, "force")))
+      && !current_impl_locked)
     {
       /* 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 that function could resolve this problem
-        itself.  */
-      set_progress_implementation (NULL);
+        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;
     }