X-Git-Url: http://sjero.net/git/?p=wget;a=blobdiff_plain;f=src%2Fprogress.c;h=6cb22d70ecb53304c6b2c71b9755d03ffa14f0ff;hp=77053e7b9524133d78de8d180ac106f5984c0b74;hb=38a7829dcb4eb5dba28dbf0f05c6a80fea9217f8;hpb=53ca4879b98a6097b5e4b8bb0df28c8d9b5e3593 diff --git a/src/progress.c b/src/progress.c index 77053e7b..6cb22d70 100644 --- a/src/progress.c +++ b/src/progress.c @@ -1,11 +1,12 @@ /* Download progress. - Copyright (C) 2001-2005 Free Software Foundation, Inc. + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, + 2010, 2011 Free Software Foundation, Inc. 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 +the Free Software Foundation; either version 3 of the License, or (at your option) any later version. GNU Wget is distributed in the hope that it will be useful, @@ -14,31 +15,31 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with Wget; if not, write to the Free Software Foundation, Inc., -51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +along with Wget. If not, see . -In addition, as a special exception, the Free Software Foundation -gives permission to link the code of its release of Wget with the -OpenSSL project's "OpenSSL" library (or with modified versions of it -that use the same license as the "OpenSSL" library), and distribute -the linked executables. You must obey the GNU General Public License -in all respects for all of the code used other than "OpenSSL". If you -modify this file, you may extend this exception to your version of the -file, but you are not obligated to do so. If you do not wish to do -so, delete this exception statement from your version. */ +Additional permission under GNU GPL version 3 section 7 -#include +If you modify this program, or any covered work, by linking or +combining it with the OpenSSL project's OpenSSL library (or a +modified version of that library), containing parts covered by the +terms of the OpenSSL or SSLeay licenses, the Free Software Foundation +grants you additional permission to convey the resulting work. +Corresponding Source for a non-source form of such a combination +shall include the source code for the parts of OpenSSL used as well +as that of the covered work. */ + +#include "wget.h" #include #include #include #include -#ifdef HAVE_UNISTD_H -# include -#endif +#include #include +#ifdef HAVE_WCHAR_H +# include +#endif -#include "wget.h" #include "progress.h" #include "utils.h" #include "retr.h" @@ -46,27 +47,30 @@ so, delete this exception statement from your version. */ 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 *); }; /* 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_draw (void *); static void dot_set_params (const 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 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; @@ -90,10 +94,10 @@ static int current_impl_locked; bool valid_progress_implementation_p (const char *name) { - int i; + size_t i; struct progress_implementation *pi = implementations; char *colon = strchr (name, ':'); - int namelen = colon ? colon - name : strlen (name); + size_t namelen = colon ? (size_t) (colon - name) : strlen (name); for (i = 0; i < countof (implementations); i++, pi++) if (!strncmp (pi->name, name, namelen)) @@ -106,7 +110,7 @@ valid_progress_implementation_p (const char *name) void set_progress_implementation (const char *name) { - int i, namelen; + size_t i, namelen; struct progress_implementation *pi = implementations; const char *colon; @@ -114,23 +118,23 @@ set_progress_implementation (const char *name) name = DEFAULT_PROGRESS_IMPLEMENTATION; colon = strchr (name, ':'); - namelen = colon ? colon - name : strlen (name); + namelen = colon ? (size_t) (colon - name) : strlen (name); for (i = 0; i < countof (implementations); i++, pi++) 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 - want to give the implementation a chance to set up some - things it needs to run. */ - ++colon; - - if (pi->set_params) - pi->set_params (colon); - return; + current_impl = pi; + current_impl_locked = 0; + + if (colon) + /* We call pi->set_params even if colon is NULL because we + want to give the implementation a chance to set up some + things it needs to run. */ + ++colon; + + if (pi->set_params) + pi->set_params (colon); + return; } abort (); } @@ -150,17 +154,17 @@ 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) { if (!current_impl_locked) - set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION); + set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION); 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 @@ -169,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; } @@ -181,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 @@ -195,16 +200,17 @@ progress_finish (void *progress, double dltime) /* Dot-printing. */ struct dot_progress { - wgint initial_length; /* how many bytes have been downloaded - previously. */ - wgint total_length; /* expected total byte count when the - download finishes */ + wgint initial_length; /* how many bytes have been downloaded + previously. */ + wgint total_length; /* expected total byte count when the + download finishes */ - int accumulated; /* number of bytes accumulated after - the last printed dot */ + int accumulated; /* number of bytes accumulated after + the last printed dot */ - int rows; /* number of rows printed so far */ - int dots; /* number of dots printed in this row */ + double dltime; /* download time so far */ + int rows; /* number of rows printed so far */ + int dots; /* number of dots printed in this row */ double last_timer_value; }; @@ -212,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; @@ -227,29 +233,29 @@ dot_create (wgint initial, wgint total) wgint skipped = dp->initial_length - remainder; if (skipped) - { - wgint skipped_k = skipped / 1024; /* skipped amount in K */ - int skipped_k_len = numdigit (skipped_k); - if (skipped_k_len < 6) - skipped_k_len = 6; - - /* 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 ]"), - 2 + skipped_k_len, "", - number_to_static_string (skipped_k)); - } - - logprintf (LOG_VERBOSE, "\n%6sK", - number_to_static_string (skipped / 1024)); + { + wgint skipped_k = skipped / 1024; /* skipped amount in K */ + int skipped_k_len = numdigit (skipped_k); + if (skipped_k_len < 6) + skipped_k_len = 6; + + /* 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_PROGRESS, _("\n%*s[ skipping %sK ]"), + 2 + skipped_k_len, "", + number_to_static_string (skipped_k)); + } + + 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, ","); - ++dp->dots; - } + { + if (dp->dots % opt.dot_spacing == 0) + logputs (LOG_PROGRESS, " "); + logputs (LOG_PROGRESS, ","); + ++dp->dots; + } assert (dp->dots < opt.dots_in_line); dp->accumulated = remainder; @@ -285,10 +291,10 @@ print_row_stats (struct dot_progress *dp, double dltime, bool last) if (dp->total_length) { /* Round to floor value to provide gauge how much data *has* - been retrieved. 12.8% will round to 12% because the 13% mark - has not yet been reached. 100% is only shown when done. */ + 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); } { @@ -305,33 +311,35 @@ 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", - rate >= 99.95 ? 0 : rate >= 9.995 ? 1 : 2, - rate, names[units]); + logprintf (LOG_PROGRESS, " %4.*f%c", + rate >= 99.95 ? 0 : rate >= 9.995 ? 1 : 2, + rate, names[units]); dp->last_timer_value = dltime; } if (!last) { /* Display ETA based on average speed. Inspired by Vladi - Belperchinov-Shabanski's "wget-new-percentage" patch. */ + Belperchinov-Shabanski's "wget-new-percentage" patch. */ if (dp->total_length) - { - wgint bytes_remaining = dp->total_length - bytes_displayed; - /* The quantity downloaded in this download run. */ - wgint bytes_sofar = bytes_displayed - dp->initial_length; - int eta = (int) (dltime * bytes_remaining / bytes_sofar + 0.5); - logprintf (LOG_VERBOSE, " %s", eta_to_human_short (eta, true)); - } + { + wgint bytes_remaining = dp->total_length - bytes_displayed; + /* The quantity downloaded in this download run. */ + wgint bytes_sofar = bytes_displayed - dp->initial_length; + double eta = dltime * bytes_remaining / bytes_sofar; + if (eta < INT_MAX - 1) + logprintf (LOG_PROGRESS, " %s", + eta_to_human_short ((int) (eta + 0.5), true)); + } } else { /* When done, print the total download time */ if (dltime >= 10) - logprintf (LOG_VERBOSE, "=%s", - eta_to_human_short ((int) (dltime + 0.5), true)); + 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)); } } @@ -339,6 +347,14 @@ print_row_stats (struct dot_progress *dp, double dltime, bool last) 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; @@ -346,25 +362,24 @@ dot_update (void *progress, wgint howmuch, double dltime) 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", - number_to_static_string (dp->rows * ROW_BYTES / 1024)); + 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) - { - ++dp->rows; - dp->dots = 0; + { + ++dp->rows; + dp->dots = 0; - print_row_stats (dp, dltime, false); - } + print_row_stats (dp, dp->dltime, false); + } } log_set_flush (true); @@ -382,13 +397,13 @@ dot_finish (void *progress, double dltime) log_set_flush (false); if (dp->dots == 0) - logprintf (LOG_VERBOSE, "\n%6sK", - number_to_static_string (dp->rows * ROW_BYTES / 1024)); + 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); @@ -416,7 +431,7 @@ dot_set_params (const char *params) if (!strcasecmp (params, "default")) { /* Default style: 1K dots, 10 dots in a cluster, 50 dots in a - line. */ + line. */ opt.dot_bytes = 1024; opt.dot_spacing = 10; opt.dots_in_line = 50; @@ -424,7 +439,7 @@ dot_set_params (const char *params) else if (!strcasecmp (params, "binary")) { /* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots - (384K) in a line. */ + (384K) in a line. */ opt.dot_bytes = 8192; opt.dot_spacing = 16; opt.dots_in_line = 48; @@ -432,7 +447,7 @@ dot_set_params (const char *params) else if (!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. */ + 64K, 8 dots in a cluster, 6 clusters (3M) in a line. */ opt.dot_bytes = 65536L; opt.dot_spacing = 8; opt.dots_in_line = 48; @@ -440,16 +455,16 @@ dot_set_params (const char *params) else if (!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 - line. */ + each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a + line. */ opt.dot_bytes = (1L << 20); opt.dot_spacing = 8; opt.dots_in_line = 32; } else fprintf (stderr, - _("Invalid dot style specification `%s'; leaving unchanged.\n"), - params); + _("Invalid dot style specification %s; leaving unchanged.\n"), + quote (params)); } /* "Thermometer" (bar) progress. */ @@ -492,27 +507,29 @@ static volatile sig_atomic_t received_sigwinch; #define ETA_REFRESH_INTERVAL 0.99 struct bar_progress { - wgint initial_length; /* how many bytes have been downloaded - previously. */ - wgint total_length; /* expected total byte count when the - download finishes */ - wgint count; /* bytes downloaded so far */ - - double last_screen_update; /* time of the last screen update, - measured since the beginning of - download. */ - - 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; /* counter used for drawing the - progress bar where the total size - is not known. */ + 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 + download finishes */ + wgint count; /* bytes downloaded so far */ + + double last_screen_update; /* time of the last screen update, + 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 + 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; /* counter used for drawing the + progress bar where the total size + is not known. */ /* The following variables (kept in a struct for namespace reasons) keep track of recent download speeds. See bar_update() for @@ -528,19 +545,19 @@ struct bar_progress { wgint total_bytes; } hist; - double recent_start; /* timestamp of beginning of current - position. */ - wgint recent_bytes; /* bytes downloaded so far. */ + double recent_start; /* timestamp of beginning of current + position. */ + wgint recent_bytes; /* bytes downloaded so far. */ - bool stalled; /* set when no data arrives for longer - than STALL_START_TIME, then reset - when new data arrives. */ + bool stalled; /* set when no data arrives for longer + than STALL_START_TIME, then reset + when new data arrives. */ /* create_image() uses these to make sure that ETA information doesn't flicker. */ - double last_eta_time; /* time of the last update to download - speed and ETA, measured since the - beginning of download. */ + double last_eta_time; /* time of the last update to download + speed and ETA, measured since the + beginning of download. */ int last_eta_value; }; @@ -548,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); @@ -559,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. */ @@ -566,16 +584,17 @@ bar_create (wgint initial, wgint total) { screen_width = determine_screen_width (); if (!screen_width) - screen_width = DEFAULT_SCREEN_WIDTH; + screen_width = DEFAULT_SCREEN_WIDTH; else if (screen_width < MINIMUM_SCREEN_WIDTH) - screen_width = MINIMUM_SCREEN_WIDTH; + screen_width = MINIMUM_SCREEN_WIDTH; received_sigwinch = 0; } /* - 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); + /* + enough space for the terminating zero, and hopefully enough room + * for multibyte characters. */ + bp->buffer = xmalloc (bp->width + 100); logputs (LOG_VERBOSE, "\n"); @@ -591,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) @@ -604,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. */ @@ -612,25 +638,25 @@ bar_update (void *progress, wgint howmuch, double dltime) int old_width = screen_width; screen_width = determine_screen_width (); if (!screen_width) - screen_width = DEFAULT_SCREEN_WIDTH; + screen_width = DEFAULT_SCREEN_WIDTH; else if (screen_width < MINIMUM_SCREEN_WIDTH) - screen_width = MINIMUM_SCREEN_WIDTH; + screen_width = MINIMUM_SCREEN_WIDTH; if (screen_width != old_width) - { - bp->width = screen_width - 1; - bp->buffer = xrealloc (bp->buffer, bp->width + 1); - force_screen_update = true; - } + { + bp->width = screen_width - 1; + bp->buffer = xrealloc (bp->buffer, bp->width + 100); + force_screen_update = true; + } 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 @@ -646,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); @@ -688,18 +715,18 @@ update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime) if (howmuch == 0) { /* If we're not downloading anything, we might be stalling, - i.e. not downloading anything for an extended period of time. - Since 0-reads do not enter the history ring, recent_age - effectively measures the time since last read. */ + i.e. not downloading anything for an extended period of time. + Since 0-reads do not enter the history ring, recent_age + effectively measures the time since last read. */ if (recent_age >= STALL_START_TIME) - { - /* 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 = true; - xzero (*hist); - bp->recent_bytes = 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 = true; + xzero (*hist); + bp->recent_bytes = 0; + } return; } @@ -709,11 +736,11 @@ update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime) if (bp->stalled) { 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. - Start with an arbitrary (but more reasonable) time value and - let it level out. */ + /* "recent_age" includes 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. + Start with an arbitrary (but more reasonable) time value and + let it level out. */ recent_age = 1; } @@ -746,8 +773,8 @@ update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime) double sumt = 0, sumb = 0; for (i = 0; i < DLSPEED_HISTORY_SIZE; i++) { - sumt += hist->times[i]; - sumb += hist->bytes[i]; + sumt += hist->times[i]; + sumb += hist->bytes[i]; } assert (sumb == hist->total_bytes); /* We can't use assert(sumt==hist->total_time) because some @@ -761,9 +788,76 @@ update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime) #endif } -#define APPEND_LITERAL(s) do { \ - memcpy (p, s, sizeof (s) - 1); \ - p += sizeof (s) - 1; \ +#if USE_NLS_PROGRESS_BAR +static int +count_cols (const char *mbs) +{ + wchar_t wc; + int bytes; + int remaining = strlen(mbs); + int cols = 0; + int wccols; + + while (*mbs != '\0') + { + bytes = mbtowc (&wc, mbs, remaining); + assert (bytes != 0); /* Only happens when *mbs == '\0' */ + if (bytes == -1) + { + /* Invalid sequence. We'll just have to fudge it. */ + return cols + remaining; + } + mbs += bytes; + remaining -= bytes; + wccols = wcwidth(wc); + cols += (wccols == -1? 1 : wccols); + } + return cols; +} +#else +# define count_cols(mbs) ((int)(strlen(mbs))) +#endif + +static const char * +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_trans; + static int bytes_cols_diff; + if (eta_trans == NULL) + { + int nbytes; + int ncols; + +#if USE_NLS_PROGRESS_BAR + eta_trans = _(eta_str); +#else + eta_trans = eta_str; +#endif + + /* Determine the number of bytes used in the translated string, + * versus the number of columns used. This is to figure out how + * many spaces to add at the end to pad to the full line width. + * + * We'll store the difference between the number of bytes and + * number of columns, so that removing this from the string length + * will reveal the total number of columns in the progress bar. */ + nbytes = strlen (eta_trans); + ncols = count_cols (eta_trans); + bytes_cols_diff = nbytes - ncols; + } + + if (bcd != NULL) + *bcd = bytes_cols_diff; + + return eta_trans; +} + +#define APPEND_LITERAL(s) do { \ + memcpy (p, s, sizeof (s) - 1); \ + p += sizeof (s) - 1; \ } while (0) /* Use move_to_end (s) to get S to point the end of the string (the @@ -774,20 +868,28 @@ update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime) #ifndef MAX # define MAX(a, b) ((a) >= (b) ? (a) : (b)) #endif +#ifndef MIN +# define MIN(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_LEN = 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 = strlen (size_grouped); + 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_len = strlen (bp->f_download); /* The progress bar should look like this: - xx% [=======> ] nn,nnn 12.34K/s eta 36m 51s + file xx% [=======> ] nn,nnn 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 @@ -796,20 +898,46 @@ 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 MAX_FILENAME_LEN 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.5K/s" - download rate - 8 chars - " eta 36m 51s" - ETA - 13 chars + " 12.5KB/s" - download rate - 9 chars + " 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 + 13); + int progress_size = bp->width - (MAX_FILENAME_LEN + 1 + 4 + 2 + dlbytes_size + 8 + 14); + + /* The difference between the number of bytes used, + and the number of columns used. */ + int bytes_cols_diff = 0; if (progress_size < 5) progress_size = 0; + if (orig_filename_len <= MAX_FILENAME_LEN) + { + int padding = MAX_FILENAME_LEN - orig_filename_len; + sprintf (p, "%s ", bp->f_download); + p += orig_filename_len + 1; + for (;padding;padding--) + *p++ = ' '; + } + else + { + int offset; + + if (orig_filename_len > MAX_FILENAME_LEN) + offset = ((int) bp->tick) % (orig_filename_len - MAX_FILENAME_LEN); + else + offset = 0; + memcpy (p, bp->f_download + offset, MAX_FILENAME_LEN); + p += MAX_FILENAME_LEN; + *p++ = ' '; + } + /* "xx% " */ if (bp->total_length > 0) { @@ -817,9 +945,9 @@ create_image (struct bar_progress *bp, double dl_total_time, bool done) assert (percentage <= 100); if (percentage < 100) - sprintf (p, "%2d%% ", percentage); + sprintf (p, "%2d%% ", percentage); else - strcpy (p, "100%"); + strcpy (p, "100%"); p += 4; } else @@ -844,127 +972,143 @@ create_image (struct bar_progress *bp, double dl_total_time, bool done) begin = p; /* Print the initial portion of the download with '+' chars, the - rest with '=' and one '>'. */ + rest with '=' and one '>'. */ for (i = 0; i < insz; i++) - *p++ = '+'; + *p++ = '+'; dlsz -= insz; if (dlsz > 0) - { - for (i = 0; i < dlsz - 1; i++) - *p++ = '='; - *p++ = '>'; - } + { + for (i = 0; i < dlsz - 1; i++) + *p++ = '='; + *p++ = '>'; + } while (p - begin < progress_size) - *p++ = ' '; + *p++ = ' '; *p++ = ']'; } else if (progress_size) { /* If we can't draw a real progress bar, then at least show - *something* to the user. */ + *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; + pos = ind + 1; else - pos = progress_size - (ind - progress_size + 5); + 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++ = ' '; - } + { + if (i == pos - 1) *p++ = '<'; + else if (i == pos ) *p++ = '='; + else if (i == pos + 1) *p++ = '>'; + else + *p++ = ' '; + } *p++ = ']'; - ++bp->tick; } + ++bp->tick; /* " 234,567,890" */ - sprintf (p, " %-11s", size_grouped); + sprintf (p, " %s", size_grouped); move_to_end (p); + /* Pad with spaces to 11 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; + size_grouped_pad > 0; + --size_grouped_pad) + { + *p++ = ' '; + } - /* " 12.52K/s" */ + /* " 12.52Kb/s or 12.52KB/s" */ if (hist->total_time > 0 && hist->total_bytes) { - static const char *short_units[] = { "B/s", "K/s", "M/s", "G/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. */ + recent data that hasn't made it to the ring yet. */ 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, " %4.*f%s", dlspeed >= 99.95 ? 0 : dlspeed >= 9.995 ? 1 : 2, - dlspeed, short_units[units]); + sprintf (p, "%4.*f%s", dlspeed >= 99.95 ? 0 : dlspeed >= 9.995 ? 1 : 2, + dlspeed, !opt.report_bps ? short_units[units] : short_units_bits[units]); move_to_end (p); } else - APPEND_LITERAL (" --.-K/s"); + APPEND_LITERAL ("--.-KB/s"); if (!done) { /* " eta ..m ..s"; wait for three seconds before displaying the ETA. - That's because the ETA value needs a while to become - reliable. */ + That's because the ETA value needs a while to become + reliable. */ if (bp->total_length > 0 && bp->count > 0 && dl_total_time > 3) - { - int eta; - - /* Don't change the value of ETA more than approximately once - per second; doing so would cause flashing without providing - any value to the user. */ - if (bp->total_length != size - && bp->last_eta_value != 0 - && dl_total_time - bp->last_eta_time < ETA_REFRESH_INTERVAL) - eta = bp->last_eta_value; - else - { - /* Calculate ETA using the average download speed to predict - the future speed. If you want to use a speed averaged - over a more recent period, replace dl_total_time with - hist->total_time and bp->count with hist->total_bytes. - I found that doing that results in a very jerky and - ultimately unreliable ETA. */ - wgint bytes_remaining = bp->total_length - size; - eta = (int) (dl_total_time * bytes_remaining / bp->count + 0.5); - bp->last_eta_value = eta; - bp->last_eta_time = dl_total_time; - } - - /* Translation note: "ETA" is English-centric, but this must - be short, ideally 3 chars. Abbreviate if necessary. */ - sprintf (p, _(" eta %s"), eta_to_human_short (eta, false)); - move_to_end (p); - } + { + int eta; + + /* Don't change the value of ETA more than approximately once + per second; doing so would cause flashing without providing + any value to the user. */ + if (bp->total_length != size + && bp->last_eta_value != 0 + && dl_total_time - bp->last_eta_time < ETA_REFRESH_INTERVAL) + eta = bp->last_eta_value; + else + { + /* Calculate ETA using the average download speed to predict + the future speed. If you want to use a speed averaged + over a more recent period, replace dl_total_time with + hist->total_time and bp->count with hist->total_bytes. + I found that doing that results in a very jerky and + ultimately unreliable ETA. */ + wgint bytes_remaining = bp->total_length - size; + double eta_ = dl_total_time * bytes_remaining / bp->count; + if (eta_ >= INT_MAX - 1) + goto skip_eta; + eta = (int) (eta_ + 0.5); + bp->last_eta_value = eta; + bp->last_eta_time = dl_total_time; + } + + sprintf (p, get_eta(&bytes_cols_diff), + eta_to_human_short (eta, false)); + move_to_end (p); + } else if (bp->total_length > 0) - { - APPEND_LITERAL (" "); - } + { + skip_eta: + APPEND_LITERAL (" "); + } } else { /* When the download is done, print the elapsed time. */ + int nbytes; + int ncols; /* Note to translators: this should not take up more room than - available here. Abbreviate if necessary. */ + available here. Abbreviate if necessary. */ strcpy (p, _(" in ")); - move_to_end (p); /* not p+=6, think translations! */ + nbytes = strlen (p); + ncols = count_cols (p); + bytes_cols_diff = nbytes - ncols; + p += nbytes; if (dl_total_time >= 10) - strcpy (p, eta_to_human_short ((int) (dl_total_time + 0.5), false)); + strcpy (p, eta_to_human_short ((int) (dl_total_time + 0.5), false)); else - sprintf (p, "%ss", print_decimal (dl_total_time)); + sprintf (p, "%ss", print_decimal (dl_total_time)); move_to_end (p); } - assert (p - bp->buffer <= bp->width); - - while (p < bp->buffer + bp->width) + while (p - bp->buffer - bytes_cols_diff - size_grouped_diff < bp->width) *p++ = ' '; *p = '\0'; } @@ -976,8 +1120,8 @@ 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); } @@ -993,24 +1137,24 @@ bar_set_params (const char *params) if ((opt.lfilename #ifdef HAVE_ISATTY /* The progress bar doesn't make sense if the output is not a - TTY -- when logging to file, it is better to review the - dots. */ + TTY -- when logging to file, it is better to review the + dots. */ || !isatty (fileno (stderr)) #endif /* Normally we don't depend on terminal type because the - progress bar only uses ^M to move the cursor to the - beginning of line, which works even on dumb terminals. But - Jamie Zawinski reports that ^M and ^H tricks don't work in - Emacs shell buffers, and only make a mess. */ + progress bar only uses ^M to move the cursor to the + beginning of line, which works even on dumb terminals. But + Jamie Zawinski reports that ^M and ^H tricks don't work in + Emacs shell buffers, and only make a mess. */ || (term && 0 == strcmp (term, "emacs")) ) && !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 we provided that function a return value - indicating a failure of some sort. */ + display. #### We're recursively calling + set_progress_implementation here, which is slightly kludgy. + 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; } @@ -1018,7 +1162,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); @@ -1044,7 +1188,7 @@ progress_handle_sigwinch (int sig) static const char * eta_to_human_short (int secs, bool condensed) { - static char buf[10]; /* 8 should be enough, but just in case */ + static char buf[10]; /* 8 should be enough, but just in case */ static int last = -1; const char *space = condensed ? "" : " "; @@ -1062,7 +1206,7 @@ eta_to_human_short (int secs, bool condensed) else if (secs < 48 * 3600) sprintf (buf, "%dh%s%dm", secs / 3600, space, (secs / 60) % 60); else if (secs < 100 * 86400) - sprintf (buf, "%dd%s%dh", secs / 86400, space, (secs / 3600) % 60); + sprintf (buf, "%dd%s%dh", secs / 86400, space, (secs / 3600) % 24); else /* even (2^31-1)/86400 doesn't overflow BUF. */ sprintf (buf, "%dd", secs / 86400);