X-Git-Url: http://sjero.net/git/?a=blobdiff_plain;f=src%2Fprogress.c;h=6f24cd290e027094504b7479910e109141256f33;hb=bb0194c6e7181825cd6a1cf6440c05d9f99f6449;hp=3da69852d3267ab2895a4d2ae3f4b9d27920ee19;hpb=e3fb2ec5dfac3bcad7e8aeb36597fffce6ea7443;p=wget diff --git a/src/progress.c b/src/progress.c index 3da69852..6f24cd29 100644 --- a/src/progress.c +++ b/src/progress.c @@ -31,18 +31,12 @@ so, delete this exception statement from your version. */ #include #include -#ifdef HAVE_STRING_H -# include -#else -# include -#endif /* HAVE_STRING_H */ +#include #include #ifdef HAVE_UNISTD_H # include #endif -#ifdef HAVE_SIGNAL_H -# include -#endif +#include #include "wget.h" #include "progress.h" @@ -51,24 +45,24 @@ so, delete this exception statement from your version. */ 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 }, @@ -82,7 +76,7 @@ static int current_impl_locked; #define DEFAULT_PROGRESS_IMPLEMENTATION "bar" -/* Fallnback progress implementation should be something that works +/* Fallback progress implementation should be something that works under all display types. If you put something other than "dot" here, remember that bar_set_params tries to switch to this if we're not running on a TTY. So changing this to "bar" could cause @@ -90,10 +84,10 @@ static int current_impl_locked; #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; @@ -103,8 +97,8 @@ valid_progress_implementation_p (const char *name) 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. */ @@ -114,7 +108,7 @@ set_progress_implementation (const char *name) { int i, namelen; struct progress_implementation *pi = implementations; - char *colon; + const char *colon; if (!name) name = DEFAULT_PROGRESS_IMPLEMENTATION; @@ -169,12 +163,12 @@ progress_create (wgint initial, wgint total) 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; @@ -264,15 +258,21 @@ dot_create (wgint initial, wgint total) static void print_percentage (wgint bytes, wgint expected) { - int percentage = (int)(100.0 * bytes / expected); + /* This intentionally rounds to the floor value because it is a + measure of how much data *has* been retrieved. Therefore 12.8% + rounds to 12% because the 13% mark has not yet been reached. + Likewise, 100% is only shown when all data has been retrieved, + not before. */ + + int percentage = 100.0 * bytes / expected; logprintf (LOG_VERBOSE, "%3d%%", percentage); } 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; } @@ -285,7 +285,7 @@ dot_update (void *progress, wgint howmuch, double dltime) 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) @@ -313,7 +313,7 @@ dot_update (void *progress, wgint howmuch, double dltime) } } - log_set_flush (1); + log_set_flush (true); } /* Dot-progress backend for progress_finish. */ @@ -326,7 +326,7 @@ dot_finish (void *progress, double dltime) 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)); @@ -352,7 +352,7 @@ dot_finish (void *progress, double dltime) } logputs (LOG_VERBOSE, "\n\n"); - log_set_flush (0); + log_set_flush (false); xfree (dp); } @@ -483,7 +483,7 @@ struct bar_progress { 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. */ @@ -492,11 +492,11 @@ struct bar_progress { 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, bool); +static void display_image (char *); static void * bar_create (wgint initial, wgint total) @@ -530,19 +530,19 @@ bar_create (wgint initial, wgint total) logputs (LOG_VERBOSE, "\n"); - create_image (bp, 0); + create_image (bp, 0, false); display_image (bp->buffer); 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 @@ -570,7 +570,7 @@ bar_update (void *progress, wgint howmuch, double dltime) { bp->width = screen_width - 1; bp->buffer = xrealloc (bp->buffer, bp->width + 1); - force_screen_update = 1; + force_screen_update = true; } received_sigwinch = 0; } @@ -579,7 +579,7 @@ bar_update (void *progress, wgint howmuch, double dltime) /* 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; } @@ -594,7 +594,7 @@ bar_finish (void *progress, double 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"); @@ -647,7 +647,7 @@ update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime) /* 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; } @@ -659,7 +659,7 @@ update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime) /* 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. @@ -706,28 +706,35 @@ update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime) #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 = 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 @@ -740,11 +747,11 @@ create_image (struct bar_progress *bp, double dl_total_time) "[]" - 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) @@ -753,8 +760,7 @@ create_image (struct bar_progress *bp, double dl_total_time) /* "xx% " */ if (bp->total_length > 0) { - int percentage = (int)(100.0 * size / bp->total_length); - + int percentage = 100.0 * size / bp->total_length; assert (percentage <= 100); if (percentage < 100) @@ -828,8 +834,8 @@ create_image (struct bar_progress *bp, double dl_total_time) } /* " 234,567,890" */ - sprintf (p, " %-11s", legible (size)); - p += strlen (p); + sprintf (p, " %-11s", size_grouped); + move_to_end (p); /* " 1012.45K/s" */ if (hist->total_time && hist->total_bytes) @@ -842,67 +848,71 @@ create_image (struct bar_progress *bp, double dl_total_time) 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); @@ -918,7 +928,7 @@ create_image (struct bar_progress *bp, double dl_total_time) 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); @@ -960,10 +970,53 @@ bar_set_params (const char *params) } #ifdef SIGWINCH -RETSIGTYPE +void progress_handle_sigwinch (int sig) { received_sigwinch = 1; 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. + + If SEP is false, the separator between minutes and seconds (and + hours and minutes, etc.) is not included, shortening the display by + one additional character. This is used for dot progress. + + The display 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. create_image can call us every 200 msecs + (see bar_update) for fast downloads, but ETA will only change + once per 900 msecs. */ + 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; +}