static void
print_download_speed (struct dot_progress *dp, wgint bytes, double dltime)
{
- logprintf (LOG_VERBOSE, " %s",
- retr_rate (bytes, dltime - dp->last_timer_value, 1));
+ logprintf (LOG_VERBOSE, " %7s",
+ retr_rate (bytes, dltime - dp->last_timer_value));
dp->last_timer_value = dltime;
}
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 (struct bar_progress *, double);
+static void create_image (struct bar_progress *, double, bool);
static void display_image (char *);
static void *
logputs (LOG_VERBOSE, "\n");
- create_image (bp, 0);
+ create_image (bp, 0, false);
display_image (bp->buffer);
return bp;
/* Don't update more often than five times per second. */
return;
- create_image (bp, dltime);
+ create_image (bp, dltime, false);
display_image (bp->buffer);
bp->last_screen_update = dltime;
}
/* See bar_update() for explanation. */
bp->total_length = bp->initial_length + bp->count;
- create_image (bp, dltime);
+ create_image (bp, dltime, true);
display_image (bp->buffer);
logputs (LOG_VERBOSE, "\n\n");
#endif
}
+static const char *eta_to_human_short (int);
+
#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
+ terminating \0). This is faster than s+=strlen(s), but some people
+ 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)
+create_image (struct bar_progress *bp, double dl_total_time, bool done)
{
char *p = bp->buffer;
wgint size = bp->initial_length + bp->count;
- char *size_legible = with_thousand_seps (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", with_thousand_seps (size));
- p += strlen (p);
+ sprintf (p, " %-11s", size_grouped);
+ move_to_end (p);
/* " 1012.45K/s" */
if (hist->total_time && hist->total_bytes)
double dltime = hist->total_time + (dl_total_time - bp->recent_start);
double dlspeed = calc_rate (dlquant, dltime, &units);
sprintf (p, " %7.2f%s", dlspeed, short_units[units]);
- p += strlen (p);
+ move_to_end (p);
}
else
APPEND_LITERAL (" --.--K/s");
- /* " ETA xx:xx:xx"; 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)
+ if (!done)
{
- wgint eta;
- int eta_hrs, eta_min, eta_sec;
-
- /* 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 < 900)
- eta = bp->last_eta_value;
- else
+ /* " 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)
{
- /* 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. */
- double time_sofar = (double)dl_total_time / 1000;
- wgint bytes_remaining = bp->total_length - size;
- eta = (wgint) (time_sofar * bytes_remaining / bp->count);
- bp->last_eta_value = eta;
- bp->last_eta_time = dl_total_time;
+ 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 < 900)
+ 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. */
+ double time_sofar = (double) dl_total_time / 1000;
+ wgint bytes_remaining = bp->total_length - size;
+ eta = (int) (time_sofar * 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));
+ move_to_end (p);
}
-
- 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)
+ else if (bp->total_length > 0)
{
- /* Hours not printed: pad with three spaces. */
- APPEND_LITERAL (" ");
- sprintf (p, " ETA %02d:%02d", eta_min, eta_sec);
+ APPEND_LITERAL (" ");
}
- 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);
- }
- p += strlen (p);
}
- else if (bp->total_length > 0)
+ else
{
- no_eta:
- APPEND_LITERAL (" ");
+ /* When the download is done, print the elapsed time. */
+ double secs = dl_total_time / 1000;
+ /* 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! */
+ if (secs >= 10)
+ strcpy (p, eta_to_human_short ((int) (secs + 0.5)));
+ else
+ /* For very quick downloads show more exact timing information. */
+ sprintf (p, _("%.*fs"),
+ secs < 0.001 ? 0 : /* 0s instead of 0.000s */
+ secs < 0.01 ? 3 : /* 0.00x */
+ secs < 0.1 ? 2 : /* 0.0x */
+ 1, /* 0.x, 1.x, ..., 9.x */
+ secs);
+ move_to_end (p);
}
assert (p - bp->buffer <= bp->width);
signal (SIGWINCH, progress_handle_sigwinch);
}
#endif
+
+/* Provide a short human-readable rendition of the ETA. This is like
+ secs_to_human_time in main.c, except the output doesn't include
+ fractions (which would look silly in by nature imprecise ETA) and
+ takes less room. If the time is measured in hours, hours and
+ minutes (but not seconds) are shown; if measured in days, then days
+ and hours are shown. This ensures brevity while still displaying
+ as much as possible.
+
+ It never occupies more than 7 characters of screen space. */
+
+static const char *
+eta_to_human_short (int secs)
+{
+ static char buf[10]; /* 8 should be 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
+ /* even (2^31-1)/86400 doesn't overflow BUF. */
+ sprintf (buf, "%dd", secs / 86400);
+
+ return buf;
+}