summaryrefslogtreecommitdiff
path: root/src/progress.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/progress.c')
-rw-r--r--src/progress.c274
1 files changed, 196 insertions, 78 deletions
diff --git a/src/progress.c b/src/progress.c
index 2e888a9..132a8e5 100644
--- a/src/progress.c
+++ b/src/progress.c
@@ -36,38 +36,41 @@ as that of the covered work. */
#include <assert.h>
#include <unistd.h>
#include <signal.h>
-#ifdef HAVE_WCHAR_H
-# include <wchar.h>
-#endif
+#include <wchar.h>
+#include <mbiter.h>
#include "progress.h"
#include "utils.h"
#include "retr.h"
+#include "c-strcase.h"
struct progress_implementation {
const char *name;
bool interactive;
- void *(*create) (wgint, wgint);
+ void *(*create) (const char *, wgint, wgint);
void (*update) (void *, wgint, double);
+ void (*draw) (void *);
void (*finish) (void *, double);
- void (*set_params) (const char *);
+ void (*set_params) (char *);
};
/* Necessary forward declarations. */
-static void *dot_create (wgint, wgint);
+static void *dot_create (const char *, wgint, wgint);
static void dot_update (void *, wgint, double);
static void dot_finish (void *, double);
-static void dot_set_params (const char *);
+static void dot_draw (void *);
+static void dot_set_params (char *);
-static void *bar_create (wgint, wgint);
+static void *bar_create (const char *, wgint, wgint);
static void bar_update (void *, wgint, double);
+static void bar_draw (void *);
static void bar_finish (void *, double);
-static void bar_set_params (const char *);
+static void bar_set_params (char *);
static struct progress_implementation implementations[] = {
- { "dot", 0, dot_create, dot_update, dot_finish, dot_set_params },
- { "bar", 1, bar_create, bar_update, bar_finish, bar_set_params }
+ { "dot", 0, dot_create, dot_update, dot_draw, dot_finish, dot_set_params },
+ { "bar", 1, bar_create, bar_update, bar_draw, bar_finish, bar_set_params }
};
static struct progress_implementation *current_impl;
static int current_impl_locked;
@@ -109,7 +112,7 @@ set_progress_implementation (const char *name)
{
size_t i, namelen;
struct progress_implementation *pi = implementations;
- const char *colon;
+ char *colon;
if (!name)
name = DEFAULT_PROGRESS_IMPLEMENTATION;
@@ -151,7 +154,7 @@ progress_schedule_redirect (void)
advance. */
void *
-progress_create (wgint initial, wgint total)
+progress_create (const char *f_download, wgint initial, wgint total)
{
/* Check if the log status has changed under our feet. */
if (output_redirected)
@@ -161,7 +164,7 @@ progress_create (wgint initial, wgint total)
output_redirected = 0;
}
- return current_impl->create (initial, total);
+ return current_impl->create (f_download, initial, total);
}
/* Return true if the progress gauge is "interactive", i.e. if it can
@@ -170,7 +173,7 @@ progress_create (wgint initial, wgint total)
and current update. */
bool
-progress_interactive_p (void *progress)
+progress_interactive_p (void *progress _GL_UNUSED)
{
return current_impl->interactive;
}
@@ -182,6 +185,7 @@ void
progress_update (void *progress, wgint howmuch, double dltime)
{
current_impl->update (progress, howmuch, dltime);
+ current_impl->draw (progress);
}
/* Tell the progress gauge to clean up. Calling this will free the
@@ -192,7 +196,7 @@ progress_finish (void *progress, double dltime)
{
current_impl->finish (progress, dltime);
}
-
+
/* Dot-printing. */
struct dot_progress {
@@ -204,6 +208,7 @@ struct dot_progress {
int accumulated; /* number of bytes accumulated after
the last printed dot */
+ double dltime; /* download time so far */
int rows; /* number of rows printed so far */
int dots; /* number of dots printed in this row */
@@ -213,7 +218,7 @@ struct dot_progress {
/* Dot-progress backend for progress_create. */
static void *
-dot_create (wgint initial, wgint total)
+dot_create (const char *f_download _GL_UNUSED, wgint initial, wgint total)
{
struct dot_progress *dp = xnew0 (struct dot_progress);
dp->initial_length = initial;
@@ -237,18 +242,18 @@ dot_create (wgint initial, wgint 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[ skipping %sK ]"),
+ logprintf (LOG_PROGRESS, _("\n%*s[ skipping %sK ]"),
2 + skipped_k_len, "",
number_to_static_string (skipped_k));
}
- logprintf (LOG_VERBOSE, "\n%6sK",
+ logprintf (LOG_PROGRESS, "\n%6sK",
number_to_static_string (skipped / 1024));
for (; remainder >= dot_bytes; remainder -= dot_bytes)
{
if (dp->dots % opt.dot_spacing == 0)
- logputs (LOG_VERBOSE, " ");
- logputs (LOG_VERBOSE, ",");
+ logputs (LOG_PROGRESS, " ");
+ logputs (LOG_PROGRESS, ",");
++dp->dots;
}
assert (dp->dots < opt.dots_in_line);
@@ -289,7 +294,7 @@ print_row_stats (struct dot_progress *dp, double dltime, bool last)
been retrieved. 12.8% will round to 12% because the 13% mark
has not yet been reached. 100% is only shown when done. */
int percentage = 100.0 * bytes_displayed / dp->total_length;
- logprintf (LOG_VERBOSE, "%3d%%", percentage);
+ logprintf (LOG_PROGRESS, "%3d%%", percentage);
}
{
@@ -306,7 +311,7 @@ print_row_stats (struct dot_progress *dp, double dltime, bool last)
if (dp->rows == dp->initial_length / ROW_BYTES)
bytes_this_row -= dp->initial_length % ROW_BYTES;
rate = calc_rate (bytes_this_row, dltime - dp->last_timer_value, &units);
- logprintf (LOG_VERBOSE, " %4.*f%c",
+ logprintf (LOG_PROGRESS, " %4.*f%c",
rate >= 99.95 ? 0 : rate >= 9.995 ? 1 : 2,
rate, names[units]);
dp->last_timer_value = dltime;
@@ -323,7 +328,7 @@ print_row_stats (struct dot_progress *dp, double dltime, bool last)
wgint bytes_sofar = bytes_displayed - dp->initial_length;
double eta = dltime * bytes_remaining / bytes_sofar;
if (eta < INT_MAX - 1)
- logprintf (LOG_VERBOSE, " %s",
+ logprintf (LOG_PROGRESS, " %s",
eta_to_human_short ((int) (eta + 0.5), true));
}
}
@@ -331,10 +336,10 @@ print_row_stats (struct dot_progress *dp, double dltime, bool last)
{
/* When done, print the total download time */
if (dltime >= 10)
- logprintf (LOG_VERBOSE, "=%s",
+ logprintf (LOG_PROGRESS, "=%s",
eta_to_human_short ((int) (dltime + 0.5), true));
else
- logprintf (LOG_VERBOSE, "=%ss", print_decimal (dltime));
+ logprintf (LOG_PROGRESS, "=%ss", print_decimal (dltime));
}
}
@@ -344,21 +349,28 @@ static void
dot_update (void *progress, wgint howmuch, double dltime)
{
struct dot_progress *dp = progress;
+ dp->accumulated += howmuch;
+ dp->dltime = dltime;
+}
+
+static void
+dot_draw (void *progress)
+{
+ struct dot_progress *dp = progress;
int dot_bytes = opt.dot_bytes;
wgint ROW_BYTES = opt.dot_bytes * opt.dots_in_line;
log_set_flush (false);
- dp->accumulated += howmuch;
for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
{
if (dp->dots == 0)
- logprintf (LOG_VERBOSE, "\n%6sK",
+ logprintf (LOG_PROGRESS, "\n%6sK",
number_to_static_string (dp->rows * ROW_BYTES / 1024));
if (dp->dots % opt.dot_spacing == 0)
- logputs (LOG_VERBOSE, " ");
- logputs (LOG_VERBOSE, ".");
+ logputs (LOG_PROGRESS, " ");
+ logputs (LOG_PROGRESS, ".");
++dp->dots;
if (dp->dots >= opt.dots_in_line)
@@ -366,7 +378,7 @@ dot_update (void *progress, wgint howmuch, double dltime)
++dp->rows;
dp->dots = 0;
- print_row_stats (dp, dltime, false);
+ print_row_stats (dp, dp->dltime, false);
}
}
@@ -385,13 +397,13 @@ dot_finish (void *progress, double dltime)
log_set_flush (false);
if (dp->dots == 0)
- logprintf (LOG_VERBOSE, "\n%6sK",
+ logprintf (LOG_PROGRESS, "\n%6sK",
number_to_static_string (dp->rows * ROW_BYTES / 1024));
for (i = dp->dots; i < opt.dots_in_line; i++)
{
if (i % opt.dot_spacing == 0)
- logputs (LOG_VERBOSE, " ");
- logputs (LOG_VERBOSE, " ");
+ logputs (LOG_PROGRESS, " ");
+ logputs (LOG_PROGRESS, " ");
}
print_row_stats (dp, dltime, true);
@@ -407,7 +419,7 @@ dot_finish (void *progress, double dltime)
giga. */
static void
-dot_set_params (const char *params)
+dot_set_params (char *params)
{
if (!params || !*params)
params = opt.dot_style;
@@ -416,7 +428,7 @@ dot_set_params (const char *params)
return;
/* We use this to set the retrieval style. */
- if (!strcasecmp (params, "default"))
+ if (!c_strcasecmp (params, "default"))
{
/* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
line. */
@@ -424,7 +436,7 @@ dot_set_params (const char *params)
opt.dot_spacing = 10;
opt.dots_in_line = 50;
}
- else if (!strcasecmp (params, "binary"))
+ else if (!c_strcasecmp (params, "binary"))
{
/* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
(384K) in a line. */
@@ -432,7 +444,7 @@ dot_set_params (const char *params)
opt.dot_spacing = 16;
opt.dots_in_line = 48;
}
- else if (!strcasecmp (params, "mega"))
+ else if (!c_strcasecmp (params, "mega"))
{
/* "Mega" retrieval, for retrieving very long files; each dot is
64K, 8 dots in a cluster, 6 clusters (3M) in a line. */
@@ -440,7 +452,7 @@ dot_set_params (const char *params)
opt.dot_spacing = 8;
opt.dots_in_line = 48;
}
- else if (!strcasecmp (params, "giga"))
+ else if (!c_strcasecmp (params, "giga"))
{
/* "Giga" retrieval, for retrieving very very *very* long files;
each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
@@ -454,7 +466,7 @@ dot_set_params (const char *params)
_("Invalid dot style specification %s; leaving unchanged.\n"),
quote (params));
}
-
+
/* "Thermometer" (bar) progress. */
/* Assumed screen width if we can't find the real value. */
@@ -495,6 +507,7 @@ static volatile sig_atomic_t received_sigwinch;
#define ETA_REFRESH_INTERVAL 0.99
struct bar_progress {
+ const char *f_download; /* Filename of the downloaded file */
wgint initial_length; /* how many bytes have been downloaded
previously. */
wgint total_length; /* expected total byte count when the
@@ -505,6 +518,7 @@ struct bar_progress {
measured since the beginning of
download. */
+ double dltime; /* download time so far */
int width; /* screen width we're using at the
time the progress gauge was
created. this is different from
@@ -551,7 +565,7 @@ static void create_image (struct bar_progress *, double, bool);
static void display_image (char *);
static void *
-bar_create (wgint initial, wgint total)
+bar_create (const char *f_download, wgint initial, wgint total)
{
struct bar_progress *bp = xnew0 (struct bar_progress);
@@ -562,6 +576,7 @@ bar_create (wgint initial, wgint total)
bp->initial_length = initial;
bp->total_length = total;
+ bp->f_download = f_download;
/* Initialize screen_width if this hasn't been done or if it might
have changed, as indicated by receiving SIGWINCH. */
@@ -595,8 +610,8 @@ static void
bar_update (void *progress, wgint howmuch, double dltime)
{
struct bar_progress *bp = progress;
- bool force_screen_update = false;
+ bp->dltime = dltime;
bp->count += howmuch;
if (bp->total_length > 0
&& bp->count + bp->initial_length > bp->total_length)
@@ -608,6 +623,13 @@ bar_update (void *progress, wgint howmuch, double dltime)
bp->total_length = bp->initial_length + bp->count;
update_speed_ring (bp, howmuch, dltime);
+}
+
+static void
+bar_draw (void *progress)
+{
+ bool force_screen_update = false;
+ struct bar_progress *bp = progress;
/* If SIGWINCH (the window size change signal) been received,
determine the new screen size and update the screen. */
@@ -628,13 +650,13 @@ bar_update (void *progress, wgint howmuch, double dltime)
received_sigwinch = 0;
}
- if (dltime - bp->last_screen_update < REFRESH_INTERVAL && !force_screen_update)
+ if (bp->dltime - bp->last_screen_update < REFRESH_INTERVAL && !force_screen_update)
/* Don't update more often than five times per second. */
return;
- create_image (bp, dltime, false);
+ create_image (bp, bp->dltime, false);
display_image (bp->buffer);
- bp->last_screen_update = dltime;
+ bp->last_screen_update = bp->dltime;
}
static void
@@ -650,7 +672,8 @@ bar_finish (void *progress, double dltime)
create_image (bp, dltime, true);
display_image (bp->buffer);
- logputs (LOG_VERBOSE, "\n\n");
+ logputs (LOG_VERBOSE, "\n");
+ logputs (LOG_PROGRESS, "\n");
xfree (bp->buffer);
xfree (bp);
@@ -791,8 +814,39 @@ count_cols (const char *mbs)
}
return cols;
}
+
+static int
+cols_to_bytes (const char *mbs, const int cols, int *ncols)
+{
+ int p_cols = 0, bytes = 0;
+ mbchar_t mbc;
+ mbi_iterator_t iter;
+ mbi_init (iter, mbs, strlen(mbs));
+ while (p_cols < cols && mbi_avail (iter))
+ {
+ mbc = mbi_cur (iter);
+ p_cols += mb_width (mbc);
+ /* The multibyte character has exceeded the total number of columns we
+ * have available. The remaining bytes will be padded with a space. */
+ if (p_cols > cols)
+ {
+ p_cols -= mb_width (mbc);
+ break;
+ }
+ bytes += mb_len (mbc);
+ mbi_advance (iter);
+ }
+ *ncols = p_cols;
+ return bytes;
+}
#else
-# define count_cols(mbs) ((int)(strlen(mbs)))
+static int count_cols (const char *mbs) { return (int) strlen(mbs); }
+static int
+cols_to_bytes (const char *mbs _GL_UNUSED, const int cols, int *ncols)
+{
+ *ncols = cols;
+ return cols;
+}
#endif
static const char *
@@ -800,7 +854,7 @@ get_eta (int *bcd)
{
/* TRANSLATORS: "ETA" is English-centric, but this must
be short, ideally 3 chars. Abbreviate if necessary. */
- static const char eta_str[] = N_(" eta %s");
+ static const char eta_str[] = N_(" eta %s");
static const char *eta_trans;
static int bytes_cols_diff;
if (eta_trans == NULL)
@@ -842,26 +896,20 @@ get_eta (int *bcd)
are confused when they see strchr (s, '\0') in the code. */
#define move_to_end(s) s = strchr (s, '\0');
-#ifndef MAX
-# define MAX(a, b) ((a) >= (b) ? (a) : (b))
-#endif
-
static void
create_image (struct bar_progress *bp, double dl_total_time, bool done)
{
+ const int MAX_FILENAME_COLS = bp->width / 4;
char *p = bp->buffer;
wgint size = bp->initial_length + bp->count;
- const char *size_grouped = with_thousand_seps (size);
- int size_grouped_len = count_cols (size_grouped);
- /* Difference between num cols and num bytes: */
- int size_grouped_diff = strlen (size_grouped) - size_grouped_len;
int size_grouped_pad; /* Used to pad the field width for size_grouped. */
struct bar_progress_hist *hist = &bp->hist;
+ int orig_filename_cols = count_cols (bp->f_download);
/* The progress bar should look like this:
- xx% [=======> ] nn,nnn 12.34KB/s eta 36m 51s
+ file xx% [=======> ] nnn.nnK 12.34KB/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
@@ -870,24 +918,70 @@ create_image (struct bar_progress *bp, double dl_total_time, bool done)
It would be especially bad for the progress bar to be resized
randomly.
+ "file " - Downloaded filename - MAX_FILENAME_COLS chars + 1
"xx% " or "100%" - percentage - 4 chars
"[]" - progress bar decorations - 2 chars
- " nnn,nnn,nnn" - downloaded bytes - 12 chars or very rarely more
- " 12.5KB/s" - download rate - 9 chars
+ " nnn.nnK" - downloaded bytes - 7 chars + 1
+ " 12.5KB/s" - download rate - 8 chars + 1
" eta 36m 51s" - ETA - 14 chars
"=====>..." - progress bar - the rest
*/
- int dlbytes_size = 1 + MAX (size_grouped_len, 11);
- int progress_size = bp->width - (4 + 2 + dlbytes_size + 8 + 14);
+
+#define PROGRESS_FILENAME_LEN MAX_FILENAME_COLS + 1
+#define PROGRESS_PERCENT_LEN 4
+#define PROGRESS_DECORAT_LEN 2
+#define PROGRESS_FILESIZE_LEN 7 + 1
+#define PROGRESS_DWNLOAD_RATE 8 + 1
+#define PROGRESS_ETA_LEN 14
+
+ int progress_size = bp->width - (PROGRESS_FILENAME_LEN + PROGRESS_PERCENT_LEN +
+ PROGRESS_DECORAT_LEN + PROGRESS_FILESIZE_LEN +
+ PROGRESS_DWNLOAD_RATE + PROGRESS_ETA_LEN);
/* The difference between the number of bytes used,
and the number of columns used. */
int bytes_cols_diff = 0;
+ int cols_diff;
+ const char *down_size;
if (progress_size < 5)
progress_size = 0;
+ if (orig_filename_cols <= MAX_FILENAME_COLS)
+ {
+ int padding = MAX_FILENAME_COLS - orig_filename_cols;
+ sprintf (p, "%s ", bp->f_download);
+ p += orig_filename_cols + 1;
+ for (;padding;padding--)
+ *p++ = ' ';
+ }
+ else
+ {
+ int offset_cols;
+ int bytes_in_filename, offset_bytes, col;
+ int *cols_ret = &col;
+ int padding;
+
+#define MIN_SCROLL_TEXT 5
+ if ((orig_filename_cols > MAX_FILENAME_COLS + MIN_SCROLL_TEXT) &&
+ !opt.noscroll &&
+ !done)
+ offset_cols = ((int) bp->tick) % (orig_filename_cols - MAX_FILENAME_COLS + 1);
+ else
+ offset_cols = 0;
+ offset_bytes = cols_to_bytes (bp->f_download, offset_cols, cols_ret);
+ bytes_in_filename = cols_to_bytes (bp->f_download + offset_bytes,
+ MAX_FILENAME_COLS,
+ cols_ret);
+ memcpy (p, bp->f_download + offset_bytes, bytes_in_filename);
+ p += bytes_in_filename;
+ padding = MAX_FILENAME_COLS - *cols_ret;
+ for (;padding;padding--)
+ *p++ = ' ';
+ *p++ = ' ';
+ }
+
/* "xx% " */
if (bp->total_length > 0)
{
@@ -895,7 +989,7 @@ create_image (struct bar_progress *bp, double dl_total_time, bool done)
assert (percentage <= 100);
if (percentage < 100)
- sprintf (p, "%2d%% ", percentage);
+ sprintf (p, "%3d%%", percentage);
else
strcpy (p, "100%");
p += 4;
@@ -961,16 +1055,23 @@ create_image (struct bar_progress *bp, double dl_total_time, bool done)
}
*p++ = ']';
- ++bp->tick;
}
+ ++bp->tick;
- /* " 234,567,890" */
- sprintf (p, " %s", size_grouped);
+ /* " 234.56M" */
+ down_size = human_readable (size, 1000, 2);
+ cols_diff = 7 - count_cols (down_size);
+ while (cols_diff > 0)
+ {
+ *p++=' ';
+ cols_diff--;
+ }
+ sprintf (p, " %s", down_size);
move_to_end (p);
- /* Pad with spaces to 11 chars for the size_grouped field;
+ /* Pad with spaces to 7 chars for the size_grouped field;
* couldn't use the field width specifier in sprintf, because
* it counts in bytes, not characters. */
- for (size_grouped_pad = 11 - size_grouped_len;
+ for (size_grouped_pad = PROGRESS_FILESIZE_LEN - 7;
size_grouped_pad > 0;
--size_grouped_pad)
{
@@ -980,8 +1081,8 @@ create_image (struct bar_progress *bp, double dl_total_time, bool done)
/* " 12.52Kb/s or 12.52KB/s" */
if (hist->total_time > 0 && hist->total_bytes)
{
- static const char *short_units[] = { "B/s", "KB/s", "MB/s", "GB/s" };
- static const char *short_units_bits[] = { "b/s", "Kb/s", "Mb/s", "Gb/s" };
+ static const char *short_units[] = { " B/s", "KB/s", "MB/s", "GB/s" };
+ static const char *short_units_bits[] = { " b/s", "Kb/s", "Mb/s", "Gb/s" };
int units = 0;
/* Calculate the download speed using the history ring and
recent data that hasn't made it to the ring yet. */
@@ -993,7 +1094,7 @@ create_image (struct bar_progress *bp, double dl_total_time, bool done)
move_to_end (p);
}
else
- APPEND_LITERAL (" --.-K/s");
+ APPEND_LITERAL (" --.-KB/s");
if (!done)
{
@@ -1058,9 +1159,18 @@ create_image (struct bar_progress *bp, double dl_total_time, bool done)
move_to_end (p);
}
- while (p - bp->buffer - bytes_cols_diff - size_grouped_diff < bp->width)
+ while (p - bp->buffer - bytes_cols_diff < bp->width)
*p++ = ' ';
*p = '\0';
+
+ /* 2014-11-14 Darshit Shah <darnir@gmail.com>
+ * Assert that the length of the progress bar is lesser than the size of the
+ * screen with which we are dealing. This assertion *MUST* always be removed
+ * from the release code since we do not want Wget to crash and burn when the
+ * assertion fails. Instead Wget should continue downloading and display a
+ * horrible and irritating progress bar that spams the screen with newlines.
+ */
+ assert (count_cols (bp->buffer) <= bp->width + 1);
}
/* Print the contents of the buffer as a one-line ASCII "image" so
@@ -1070,19 +1180,27 @@ static void
display_image (char *buf)
{
bool old = log_set_save_context (false);
- logputs (LOG_VERBOSE, "\r");
- logputs (LOG_VERBOSE, buf);
+ logputs (LOG_PROGRESS, "\r");
+ logputs (LOG_PROGRESS, buf);
log_set_save_context (old);
}
static void
-bar_set_params (const char *params)
+bar_set_params (char *params)
{
char *term = getenv ("TERM");
- if (params
- && 0 == strcmp (params, "force"))
- current_impl_locked = 1;
+ if (params)
+ {
+ char *param = strtok (params, ":");
+ do
+ {
+ if (0 == strcmp (param, "force"))
+ current_impl_locked = 1;
+ else if (0 == strcmp (param, "noscroll"))
+ opt.noscroll = true;
+ } while ((param = strtok (NULL, ":")) != NULL);
+ }
if ((opt.lfilename
#ifdef HAVE_ISATTY
@@ -1112,7 +1230,7 @@ bar_set_params (const char *params)
#ifdef SIGWINCH
void
-progress_handle_sigwinch (int sig)
+progress_handle_sigwinch (int sig _GL_UNUSED)
{
received_sigwinch = 1;
signal (SIGWINCH, progress_handle_sigwinch);