/* Download progress.
- Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 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.
You should have received a copy of the GNU General Public License
along with Wget. If not, see <http://www.gnu.org/licenses/>.
-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
+
+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 <stdlib.h>
#include <string.h>
#include <assert.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
+#include <unistd.h>
#include <signal.h>
+#ifdef HAVE_WCHAR_H
+# include <wchar.h>
+#endif
#include "progress.h"
#include "utils.h"
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))
void
set_progress_implementation (const char *name)
{
- int i, namelen;
+ size_t i, namelen;
struct progress_implementation *pi = implementations;
const char *colon;
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))
}
else
fprintf (stderr,
- _("Invalid dot style specification `%s'; leaving unchanged.\n"),
- params);
+ _("Invalid dot style specification %s; leaving unchanged.\n"),
+ quote (params));
}
\f
/* "Thermometer" (bar) progress. */
/* - 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");
if (screen_width != old_width)
{
bp->width = screen_width - 1;
- bp->buffer = xrealloc (bp->buffer, bp->width + 1);
+ bp->buffer = xrealloc (bp->buffer, bp->width + 100);
force_screen_update = true;
}
received_sigwinch = 0;
if (bp->stalled)
{
bp->stalled = false;
- /* "recent_age" includes the the entired stalled period, which
+ /* "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
#endif
}
+#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; \
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;
/* The progress bar should look like this:
- xx% [=======> ] nn,nnn 12.34K/s eta 36m 51s
+ 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
"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 - (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;
}
/* " 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. */
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]);
+ dlspeed, !opt.report_bps ? short_units[units] : short_units_bits[units]);
move_to_end (p);
}
else
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));
+ sprintf (p, get_eta(&bytes_cols_diff),
+ eta_to_human_short (eta, false));
move_to_end (p);
}
else if (bp->total_length > 0)
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. */
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));
else
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';
}
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);