#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"
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. */
-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 },
#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;
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. */
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;
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)
}
}
- log_set_flush (1);
+ log_set_flush (true);
}
/* Dot-progress backend for progress_finish. */
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));
}
logputs (LOG_VERBOSE, "\n\n");
- log_set_flush (0);
+ log_set_flush (false);
xfree (dp);
}
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. */
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)
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;
- int force_screen_update = 0;
+ bool force_screen_update = false;
bp->count += howmuch;
if (bp->total_length > 0
{
bp->width = screen_width - 1;
bp->buffer = xrealloc (bp->buffer, bp->width + 1);
- force_screen_update = 1;
+ force_screen_update = true;
}
received_sigwinch = 0;
}
/* 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;
}
/* 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.
#endif
}
+static const char *eta_to_human (int);
+
#define APPEND_LITERAL(s) do { \
memcpy (p, s, sizeof (s) - 1); \
p += sizeof (s) - 1; \
char *p = bp->buffer;
wgint size = bp->initial_length + bp->count;
- char *size_legible = legible (size);
- int size_legible_len = strlen (size_legible);
+ const char *size_grouped = with_thousand_seps (size);
+ int size_grouped_len = strlen (size_grouped);
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
"[]" - 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
*/
- int dlbytes_size = 1 + MAX (size_legible_len, 11);
+ int dlbytes_size = 1 + MAX (size_grouped_len, 11);
int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);
if (progress_size < 5)
if (bp->total_length > 0)
{
int percentage = (int)(100.0 * size / bp->total_length);
-
assert (percentage <= 100);
if (percentage < 100)
}
/* " 234,567,890" */
- sprintf (p, " %-11s", legible (size));
+ sprintf (p, " %-11s", size_grouped);
p += strlen (p);
/* " 1012.45K/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)
{
- 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
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;
- 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;
}
- 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 (" ");
}
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);
}
#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;
+}