]> sjero.net Git - wget/blobdiff - src/progress.c
[svn] Logging system bugfixes and improvements.
[wget] / src / progress.c
index e010d2d34ee6662219f931bfaf2e2013a534cfbd..dc96763667e4831b78b32fbde4c187dc56122d68 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
@@ -30,6 +30,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
 
 #include "wget.h"
 #include "progress.h"
@@ -61,14 +64,20 @@ static struct progress_implementation implementations[] = {
   { "bar", bar_create, bar_update, bar_finish, bar_set_params }
 };
 static struct progress_implementation *current_impl;
+static int current_impl_locked;
+
+/* Progress implementation used by default.  Can be overriden in
+   wgetrc or by the fallback one.  */
+
+#define DEFAULT_PROGRESS_IMPLEMENTATION "bar"
 
-/* Default progress implementation should be something that works
+/* 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 +115,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 +130,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,6 +147,14 @@ 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);
 }
 
@@ -195,8 +221,8 @@ dot_create (long initial, long total)
          /* 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%s",
-                    2 + skipped_k_len, "", _("[ skipping %dK ]"));
+         logprintf (LOG_VERBOSE, _("\n%*s[ skipping %dK ]"),
+                    2 + skipped_k_len, "", skipped_k);
        }
 
       logprintf (LOG_VERBOSE, "\n%5ldK", skipped / 1024);
@@ -255,15 +281,16 @@ dot_update (void *progress, long howmuch, long dltime)
       ++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),
-                               dltime);
+         print_download_speed (dp, row_qty, dltime);
        }
     }
 
@@ -282,6 +309,8 @@ dot_finish (void *progress, long dltime)
 
   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)
@@ -296,25 +325,30 @@ dot_finish (void *progress, long dltime)
                        dp->total_length);
     }
 
-  print_download_speed (dp, dp->dots * dot_bytes
-                       + dp->accumulated
-                       - dp->initial_length % row_bytes,
-                       dltime);
-  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);
 
   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;
 
@@ -378,11 +412,14 @@ struct bar_progress {
 
   long last_update;            /* time of the last screen update. */
 
-  int width;                   /* screen width at the time the
-                                  progress gauge was created. */
+  int width;                   /* screen width we're using at the
+                                  time the progress gauge was
+                                  created.  this is different from
+                                  the screen_width global variable in
+                                  that the latter can be changed by a
+                                  signal. */
   char *buffer;                        /* buffer where the bar "image" is
                                   stored. */
-
   int tick;
 };
 
@@ -398,10 +435,13 @@ bar_create (long initial, long total)
 
   bp->initial_length = initial;
   bp->total_length   = total;
-  bp->width = screen_width;
+
+  /* - 1 because we don't want to use the last screen column. */
+  bp->width = screen_width - 1;
+  /* + 1 for the terminating zero. */
   bp->buffer = xmalloc (bp->width + 1);
 
-  logputs (LOG_VERBOSE, "\n\n");
+  logputs (LOG_VERBOSE, "\n");
 
   create_image (bp, 0);
   display_image (bp->buffer);
@@ -425,14 +465,15 @@ bar_update (void *progress, long howmuch, long dltime)
        equal to the expected size doesn't abort.  */
     bp->total_length = bp->count + bp->initial_length;
 
-  if (screen_width != bp->width)
+  if (screen_width - 1 != bp->width)
     {
-      bp->width = screen_width;
+      bp->width = screen_width - 1;
       bp->buffer = xrealloc (bp->buffer, bp->width + 1);
+      force_update = 1;
     }
 
   if (dltime - bp->last_update < 200 && !force_update)
-    /* Don't update more often than every half a second. */
+    /* Don't update more often than five times per second. */
     return;
 
   bp->last_update = dltime;
@@ -461,26 +502,44 @@ bar_finish (void *progress, long dltime)
   xfree (bp);
 }
 
+#define APPEND_LITERAL(s) do {                 \
+  memcpy (p, s, sizeof (s) - 1);               \
+  p += sizeof (s) - 1;                         \
+} while (0)
+
+#ifndef MAX
+# define MAX(a, b) ((a) >= (b) ? (a) : (b))
+#endif
+
 static void
 create_image (struct bar_progress *bp, long dltime)
 {
   char *p = bp->buffer;
   long size = bp->initial_length + bp->count;
 
-  /* The progress bar should look like this:
-     xx% [=======>             ] nn.nnn rrK/s ETA 00:00
+  char *size_legible = legible (size);
+  int size_legible_len = strlen (size_legible);
 
-     Calculate its geometry:
-
-     "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
+  /* The progress bar should look like this:
+     xx% [=======>             ] nn,nnn 12.34K/s ETA 00:00
+
+     Calculate the geometry.  The idea is to assign as much room as
+     possible to the progress bar.  The other idea is to never let
+     things "jitter", i.e. pad elements that vary in size so that
+     their variance does not affect the placement of other elements.
+     It would be especially bad for the progress bar to be resized
+     randomly.
+
+     "xx% " or "100%"  - percentage               - 4 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
+
+     "=====>..."       - progress bar             - the rest
   */
-  int progress_size = screen_width - (4 + 2 + 14 + 11 + 13);
+  int dlbytes_size = 1 + MAX (size_legible_len, 11);
+  int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);
 
   if (progress_size < 5)
     progress_size = 0;
@@ -499,12 +558,7 @@ create_image (struct bar_progress *bp, long dltime)
       p += 4;
     }
   else
-    {
-      *p++ = ' ';
-      *p++ = ' ';
-      *p++ = ' ';
-      *p++ = ' ';
-    }
+    APPEND_LITERAL ("    ");
 
   /* The progress bar: "[====>      ]" */
   if (progress_size && bp->total_length > 0)
@@ -558,12 +612,8 @@ create_image (struct bar_progress *bp, long dltime)
       ++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));
+  /* " 234,567,890" */
+  sprintf (p, " %-11s", legible (size));
   p += strlen (p);
 
   /* " 1012.45K/s" */
@@ -576,10 +626,7 @@ create_image (struct bar_progress *bp, long dltime)
       p += strlen (p);
     }
   else
-    {
-      strcpy (p, "   --.--K/s");
-      p += 11;
-    }
+    APPEND_LITERAL ("   --.--K/s");
 
   /* " ETA xx:xx:xx" */
   if (bp->total_length > 0 && bp->count > 0)
@@ -594,17 +641,25 @@ create_image (struct bar_progress *bp, long dltime)
       eta_min = eta / 60,   eta %= 60;
       eta_sec = eta;
 
-      /*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);*/
+      /* Pad until the end of screen.  The padding is dependent on the
+        hour value.  */
+      if (eta_hrs == 0 || eta_hrs > 99)
+       /* Hours not printed: pad with three spaces (two digits and
+          colon). */
+       APPEND_LITERAL ("   ");
+      else if (eta_hrs < 10)
+       /* Hours printed with one digit: pad with one space. */
+       *p++ = ' ';
+      else
+       /* Hours printed with two digits: we're using maximum width,
+          don't pad. */
+       ;
 
-      *p++ = ' ';
-      *p++ = 'E';
-      *p++ = 'T';
-      *p++ = 'A';
-      *p++ = ' ';
+      APPEND_LITERAL (" ETA ");
 
       if (eta_hrs > 99)
-       /* Bogus value, for whatever reason.  We must avoid overflow. */
+       /* Bogus value, probably due to a calculation overflow.  Print
+          something safe to avoid overstepping the buffer bounds. */
        sprintf (p, "--:--");
       else if (eta_hrs > 0)
        sprintf (p, "%d:%02d:%02d", eta_hrs, eta_min, eta_sec);
@@ -613,14 +668,11 @@ create_image (struct bar_progress *bp, long dltime)
       p += strlen (p);
     }
   else if (bp->total_length > 0)
-    {
-      strcpy (p, " ETA --:--");
-      p += 10;
-    }
+    APPEND_LITERAL ("    ETA --:--");
 
-  assert (p - bp->buffer <= screen_width);
+  assert (p - bp->buffer <= bp->width);
 
-  while (p < bp->buffer + screen_width)
+  while (p < bp->buffer + bp->width)
     *p++ = ' ';
   *p = '\0';
 }
@@ -631,11 +683,10 @@ create_image (struct bar_progress *bp, long dltime)
 static void
 display_image (char *buf)
 {
-  char *del_buf = alloca (screen_width + 1);
-  memset (del_buf, '\b', screen_width);
-  del_buf[screen_width] = '\0';
-  logputs (LOG_VERBOSE, del_buf);
+  int old = log_set_save_context (0);
+  logputs (LOG_VERBOSE, "\r");
   logputs (LOG_VERBOSE, buf);
+  log_set_save_context (old);
 }
 
 static void
@@ -643,6 +694,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))
@@ -650,15 +705,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;
     }
 
@@ -667,10 +721,13 @@ bar_set_params (const char *params)
     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;
+  signal (SIGWINCH, progress_handle_sigwinch);
 }
+#endif