diff -ur wget-1.9.1/src/progress.c wget-1.9.1-patrys/src/progress.c --- wget-1.9.1/src/progress.c 2003-09-23 22:48:10.000000000 +0200 +++ wget-1.9.1-patrys/src/progress.c 2004-05-28 12:32:20.964152440 +0200 @@ -69,9 +69,15 @@ static void bar_finish PARAMS ((void *, double)); static void bar_set_params PARAMS ((const char *)); +static void *porn_create PARAMS ((long, long)); +static void porn_update PARAMS ((void *, long, double)); +static void porn_finish PARAMS ((void *, double)); +static void porn_set_params PARAMS ((const char *)); + static struct progress_implementation implementations[] = { { "dot", dot_create, dot_update, dot_finish, dot_set_params }, - { "bar", bar_create, bar_update, bar_finish, bar_set_params } + { "bar", bar_create, bar_update, bar_finish, bar_set_params }, + { "porn", porn_create, porn_update, porn_finish, porn_set_params } }; static struct progress_implementation *current_impl; static int current_impl_locked; @@ -473,6 +479,7 @@ static void create_image PARAMS ((struct bar_progress *, double)); static void display_image PARAMS ((char *)); +static void porn_create_image PARAMS ((struct bar_progress *, double)); static void * bar_create (long initial, long total) @@ -888,6 +895,339 @@ screen_width = sw; } + +static void * +porn_create (long initial, long total) +{ + struct bar_progress *bp = xmalloc (sizeof (struct bar_progress)); + + memset (bp, 0, sizeof (*bp)); + + /* In theory, our callers should take care of this pathological + case, but it can sometimes happen. */ + if (initial > total) + total = initial; + + bp->initial_length = initial; + bp->total_length = total; + + /* - 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); + + logputs (LOG_VERBOSE, "\n"); + + porn_create_image (bp, 0); + display_image (bp->buffer); + + return bp; +} + +static void +porn_update (void *progress, long howmuch, double dltime) +{ + struct bar_progress *bp = progress; + int force_screen_update = 0; + + bp->count += howmuch; + if (bp->total_length > 0 + && bp->count + bp->initial_length > bp->total_length) + /* We could be downloading more than total_length, e.g. when the + server sends an incorrect Content-Length header. In that case, + adjust bp->total_length to the new reality, so that the code in + create_image() that depends on total size being smaller or + equal to the expected size doesn't abort. */ + bp->total_length = bp->initial_length + bp->count; + + update_speed_ring (bp, howmuch, dltime); + + if (screen_width - 1 != bp->width) + { + bp->width = screen_width - 1; + bp->buffer = xrealloc (bp->buffer, bp->width + 1); + force_screen_update = 1; + } + + if (dltime - bp->last_screen_update < 200 && !force_screen_update) + /* Don't update more often than five times per second. */ + return; + + porn_create_image (bp, dltime); + display_image (bp->buffer); + bp->last_screen_update = dltime; +} + +static void +porn_finish (void *progress, double dltime) +{ + struct bar_progress *bp = progress; + + if (bp->total_length > 0 + && bp->count + bp->initial_length > bp->total_length) + /* See bar_update() for explanation. */ + bp->total_length = bp->initial_length + bp->count; + + porn_create_image (bp, dltime); + display_image (bp->buffer); + + logputs (LOG_VERBOSE, "\n\n"); + + xfree (bp->buffer); + xfree (bp); +} + +static void +porn_create_image (struct bar_progress *bp, double dl_total_time) +{ + char *p = bp->buffer; + long size = bp->initial_length + bp->count; + + char *size_legible = legible (size); + int size_legible_len = strlen (size_legible); + + struct bar_progress_hist *hist = &bp->hist; + + /* The progress bar should look like this: + xx% [=======> ] nn,nnn 12.34K/s ETA 00:00 + + Calculate the geometry. The idea is to assign as much room as + possible to the progress bar. The other idea is to never let + things "jitter", i.e. pad elements that vary in size so that + their variance does not affect the placement of other elements. + It would be especially bad for the progress bar to be resized + randomly. + + "xx% " or "100%" - percentage - 4 chars + "8(_o_)" - progress bar decorations - 6 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 + + "=====D..." - progress bar - the rest + */ + int dlbytes_size = 1 + MAX (size_legible_len, 11); + int progress_size = bp->width - (4 + 6 + dlbytes_size + 11 + 13); + + int percentage = (int)(100.0 * size / bp->total_length); + + assert (percentage <= 100); + + if (progress_size < 5) + progress_size = 0; + + /* "xx% " */ + if (bp->total_length > 0) + { + + if (percentage < 100) + sprintf (p, "%2d%% ", percentage); + else + strcpy (p, "100%"); + p += 4; + } + else + APPEND_LITERAL (" "); + + /* The progress bar: "[====> ]" or "[++==> ]". */ + if (progress_size && bp->total_length > 0) + { + /* Size of the initial portion. */ + int insz = (double)bp->initial_length / bp->total_length * progress_size; + + /* Size of the downloaded portion. */ + int dlsz = (double)size / bp->total_length * progress_size; + + char *begin; + int i; + + assert (dlsz <= progress_size); + assert (insz <= dlsz); + + *p++ = '8'; + begin = p; + + /* Print the initial portion of the download with '+' chars, the + rest with '=' and one '>'. */ + for (i = 0; i < insz; i++) + *p++ = '+'; + dlsz -= insz; + if (dlsz > 0) + { + for (i = 0; i < dlsz - 1; i++) + *p++ = '='; + *p++ = 'D'; + } + + while (p - begin < progress_size) + *p++ = ' '; + *p++ = '('; + *p++ = '_'; + if (percentage < 25) + *p++ = '.'; + else if (percentage < 50) + *p++ = 'o'; + else if (percentage < 75) + *p++ = '0'; + else + *p++ = 'O'; + *p++ = '_'; + *p++ = ')'; + } + else if (progress_size) + { + /* If we can't draw a real progress bar, then at least show + *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; + else + pos = progress_size - (ind - progress_size + 5); + + *p++ = '['; + for (i = 0; i < progress_size; i++) + { + if (i == pos - 1) *p++ = '8'; + else if (i == pos ) *p++ = '='; + else if (i == pos + 1) *p++ = 'D'; + else + *p++ = ' '; + } + *p++ = ']'; + + ++bp->tick; + } + + /* " 234,567,890" */ + sprintf (p, " %-11s", legible (size)); + p += strlen (p); + + /* " 1012.45K/s" */ + if (hist->total_time && hist->total_bytes) + { + static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" }; + int units = 0; + /* Calculate the download speed using the history ring and + recent data that hasn't made it to the ring yet. */ + long 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, " %7.2f%s", dlspeed, short_units[units]); + p += strlen (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 && dl_total_time > 3000) + { + long 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 + { + /* 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; + long bytes_remaining = bp->total_length - size; + eta = (long) (time_sofar * bytes_remaining / bp->count); + bp->last_eta_value = eta; + bp->last_eta_time = dl_total_time; + } + + 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) + { + /* Hours not printed: pad with three spaces. */ + APPEND_LITERAL (" "); + sprintf (p, " ETA %02d:%02d", eta_min, eta_sec); + } + 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) + { + no_eta: + APPEND_LITERAL (" "); + } + + assert (p - bp->buffer <= bp->width); + + while (p < bp->buffer + bp->width) + *p++ = ' '; + *p = '\0'; +} + +static void +porn_set_params (const char *params) +{ + int sw; + char *term = getenv ("TERM"); + + if (params + && 0 == strcmp (params, "force")) + current_impl_locked = 1; + + 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. */ + || !isatty (fileno (stderr)) +#else + 1 +#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. */ + || (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. */ + set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION); + return; + } + + sw = determine_screen_width (); + if (sw && sw >= MINIMUM_SCREEN_WIDTH) + screen_width = sw; +} + #ifdef SIGWINCH RETSIGTYPE progress_handle_sigwinch (int sig)