2 Copyright (C) 2001, 2002 Free Software Foundation, Inc.
4 This file is part of GNU Wget.
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 GNU Wget is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Wget; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 In addition, as a special exception, the Free Software Foundation
21 gives permission to link the code of its release of Wget with the
22 OpenSSL project's "OpenSSL" library (or with modified versions of it
23 that use the same license as the "OpenSSL" library), and distribute
24 the linked executables. You must obey the GNU General Public License
25 in all respects for all of the code used other than "OpenSSL". If you
26 modify this file, you may extend this exception to your version of the
27 file, but you are not obligated to do so. If you do not wish to do
28 so, delete this exception statement from your version. */
38 #endif /* HAVE_STRING_H */
52 struct progress_implementation {
54 void *(*create) PARAMS ((long, long));
55 void (*update) PARAMS ((void *, long, long));
56 void (*finish) PARAMS ((void *, long));
57 void (*set_params) PARAMS ((const char *));
60 /* Necessary forward declarations. */
62 static void *dot_create PARAMS ((long, long));
63 static void dot_update PARAMS ((void *, long, long));
64 static void dot_finish PARAMS ((void *, long));
65 static void dot_set_params PARAMS ((const char *));
67 static void *bar_create PARAMS ((long, long));
68 static void bar_update PARAMS ((void *, long, long));
69 static void bar_finish PARAMS ((void *, long));
70 static void bar_set_params PARAMS ((const char *));
72 static struct progress_implementation implementations[] = {
73 { "dot", dot_create, dot_update, dot_finish, dot_set_params },
74 { "bar", bar_create, bar_update, bar_finish, bar_set_params }
76 static struct progress_implementation *current_impl;
77 static int current_impl_locked;
79 /* Progress implementation used by default. Can be overriden in
80 wgetrc or by the fallback one. */
82 #define DEFAULT_PROGRESS_IMPLEMENTATION "bar"
84 /* Fallnback progress implementation should be something that works
85 under all display types. If you put something other than "dot"
86 here, remember that bar_set_params tries to switch to this if we're
87 not running on a TTY. So changing this to "bar" could cause
90 #define FALLBACK_PROGRESS_IMPLEMENTATION "dot"
92 /* Return non-zero if NAME names a valid progress bar implementation.
93 The characters after the first : will be ignored. */
96 valid_progress_implementation_p (const char *name)
99 struct progress_implementation *pi = implementations;
100 char *colon = strchr (name, ':');
101 int namelen = colon ? colon - name : strlen (name);
103 for (i = 0; i < ARRAY_SIZE (implementations); i++, pi++)
104 if (!strncmp (pi->name, name, namelen))
109 /* Set the progress implementation to NAME. */
112 set_progress_implementation (const char *name)
115 struct progress_implementation *pi = implementations;
119 name = DEFAULT_PROGRESS_IMPLEMENTATION;
121 colon = strchr (name, ':');
122 namelen = colon ? colon - name : strlen (name);
124 for (i = 0; i < ARRAY_SIZE (implementations); i++, pi++)
125 if (!strncmp (pi->name, name, namelen))
128 current_impl_locked = 0;
131 /* We call pi->set_params even if colon is NULL because we
132 want to give the implementation a chance to set up some
133 things it needs to run. */
137 pi->set_params (colon);
143 static int output_redirected;
146 progress_schedule_redirect (void)
148 output_redirected = 1;
151 /* Create a progress gauge. INITIAL is the number of bytes the
152 download starts from (zero if the download starts from scratch).
153 TOTAL is the expected total number of bytes in this download. If
154 TOTAL is zero, it means that the download size is not known in
158 progress_create (long initial, long total)
160 /* Check if the log status has changed under our feet. */
161 if (output_redirected)
163 if (!current_impl_locked)
164 set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
165 output_redirected = 0;
168 return current_impl->create (initial, total);
171 /* Inform the progress gauge of newly received bytes. DLTIME is the
172 time in milliseconds since the beginning of the download. */
175 progress_update (void *progress, long howmuch, long dltime)
177 current_impl->update (progress, howmuch, dltime);
180 /* Tell the progress gauge to clean up. Calling this will free the
181 PROGRESS object, the further use of which is not allowed. */
184 progress_finish (void *progress, long dltime)
186 current_impl->finish (progress, dltime);
191 struct dot_progress {
192 long initial_length; /* how many bytes have been downloaded
194 long total_length; /* expected total byte count when the
199 int rows; /* number of rows printed so far */
200 int dots; /* number of dots printed in this row */
201 long last_timer_value;
204 /* Dot-progress backend for progress_create. */
207 dot_create (long initial, long total)
209 struct dot_progress *dp = xmalloc (sizeof (struct dot_progress));
211 memset (dp, 0, sizeof (*dp));
213 dp->initial_length = initial;
214 dp->total_length = total;
216 if (dp->initial_length)
218 int dot_bytes = opt.dot_bytes;
219 long row_bytes = opt.dot_bytes * opt.dots_in_line;
221 int remainder = (int) (dp->initial_length % row_bytes);
222 long skipped = dp->initial_length - remainder;
226 int skipped_k = (int) (skipped / 1024); /* skipped amount in K */
227 int skipped_k_len = numdigit (skipped_k);
228 if (skipped_k_len < 5)
231 /* Align the [ skipping ... ] line with the dots. To do
232 that, insert the number of spaces equal to the number of
233 digits in the skipped amount in K. */
234 logprintf (LOG_VERBOSE, _("\n%*s[ skipping %dK ]"),
235 2 + skipped_k_len, "", skipped_k);
238 logprintf (LOG_VERBOSE, "\n%5ldK", skipped / 1024);
239 for (; remainder >= dot_bytes; remainder -= dot_bytes)
241 if (dp->dots % opt.dot_spacing == 0)
242 logputs (LOG_VERBOSE, " ");
243 logputs (LOG_VERBOSE, ",");
246 assert (dp->dots < opt.dots_in_line);
248 dp->accumulated = remainder;
249 dp->rows = skipped / row_bytes;
256 print_percentage (long bytes, long expected)
258 int percentage = (int)(100.0 * bytes / expected);
259 logprintf (LOG_VERBOSE, "%3d%%", percentage);
263 print_download_speed (struct dot_progress *dp, long bytes, long dltime)
265 logprintf (LOG_VERBOSE, " %s",
266 retr_rate (bytes, dltime - dp->last_timer_value, 1));
267 dp->last_timer_value = dltime;
270 /* Dot-progress backend for progress_update. */
273 dot_update (void *progress, long howmuch, long dltime)
275 struct dot_progress *dp = progress;
276 int dot_bytes = opt.dot_bytes;
277 long row_bytes = opt.dot_bytes * opt.dots_in_line;
281 dp->accumulated += howmuch;
282 for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
285 logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
287 if (dp->dots % opt.dot_spacing == 0)
288 logputs (LOG_VERBOSE, " ");
289 logputs (LOG_VERBOSE, ".");
292 if (dp->dots >= opt.dots_in_line)
294 long row_qty = row_bytes;
295 if (dp->rows == dp->initial_length / row_bytes)
296 row_qty -= dp->initial_length % row_bytes;
301 if (dp->total_length)
302 print_percentage (dp->rows * row_bytes, dp->total_length);
303 print_download_speed (dp, row_qty, dltime);
310 /* Dot-progress backend for progress_finish. */
313 dot_finish (void *progress, long dltime)
315 struct dot_progress *dp = progress;
316 int dot_bytes = opt.dot_bytes;
317 long row_bytes = opt.dot_bytes * opt.dots_in_line;
323 logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
324 for (i = dp->dots; i < opt.dots_in_line; i++)
326 if (i % opt.dot_spacing == 0)
327 logputs (LOG_VERBOSE, " ");
328 logputs (LOG_VERBOSE, " ");
330 if (dp->total_length)
332 print_percentage (dp->rows * row_bytes
333 + dp->dots * dot_bytes
339 long row_qty = dp->dots * dot_bytes + dp->accumulated;
340 if (dp->rows == dp->initial_length / row_bytes)
341 row_qty -= dp->initial_length % row_bytes;
342 print_download_speed (dp, row_qty, dltime);
345 logputs (LOG_VERBOSE, "\n\n");
351 /* This function interprets the progress "parameters". For example,
352 if Wget is invoked with --progress=dot:mega, it will set the
353 "dot-style" to "mega". Valid styles are default, binary, mega, and
357 dot_set_params (const char *params)
359 if (!params || !*params)
360 params = opt.dot_style;
365 /* We use this to set the retrieval style. */
366 if (!strcasecmp (params, "default"))
368 /* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
370 opt.dot_bytes = 1024;
371 opt.dot_spacing = 10;
372 opt.dots_in_line = 50;
374 else if (!strcasecmp (params, "binary"))
376 /* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
378 opt.dot_bytes = 8192;
379 opt.dot_spacing = 16;
380 opt.dots_in_line = 48;
382 else if (!strcasecmp (params, "mega"))
384 /* "Mega" retrieval, for retrieving very long files; each dot is
385 64K, 8 dots in a cluster, 6 clusters (3M) in a line. */
386 opt.dot_bytes = 65536L;
388 opt.dots_in_line = 48;
390 else if (!strcasecmp (params, "giga"))
392 /* "Giga" retrieval, for retrieving very very *very* long files;
393 each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
395 opt.dot_bytes = (1L << 20);
397 opt.dots_in_line = 32;
401 _("Invalid dot style specification `%s'; leaving unchanged.\n"),
405 /* "Thermometer" (bar) progress. */
407 /* Assumed screen width if we can't find the real value. */
408 #define DEFAULT_SCREEN_WIDTH 80
410 /* Minimum screen width we'll try to work with. If this is too small,
411 create_image will overflow the buffer. */
412 #define MINIMUM_SCREEN_WIDTH 45
414 static int screen_width = DEFAULT_SCREEN_WIDTH;
416 /* Size of the history table for download speeds. */
417 #define DLSPEED_HISTORY_SIZE 30
419 /* The time interval in milliseconds below which we increase old
420 history entries rather than overwriting them. That interval
421 represents the scope of the download speed history. */
422 #define DLSPEED_HISTORY_MAX_INTERVAL 3000
424 struct bar_progress {
425 long initial_length; /* how many bytes have been downloaded
427 long total_length; /* expected total byte count when the
429 long count; /* bytes downloaded so far */
431 long last_screen_update; /* time of the last screen update. */
433 int width; /* screen width we're using at the
434 time the progress gauge was
435 created. this is different from
436 the screen_width global variable in
437 that the latter can be changed by a
439 char *buffer; /* buffer where the bar "image" is
441 int tick; /* counter used for drawing the
442 progress bar where the total size
445 /* The following variables (kept in a struct for namespace reasons)
446 keep track of recent download speeds. See bar_update() for
448 struct bar_progress_hist {
450 long times[DLSPEED_HISTORY_SIZE];
451 long bytes[DLSPEED_HISTORY_SIZE];
457 /* create_image() uses these to make sure that ETA information
459 long last_eta_time; /* time of the last update to download
464 static void create_image PARAMS ((struct bar_progress *, long));
465 static void display_image PARAMS ((char *));
468 bar_create (long initial, long total)
470 struct bar_progress *bp = xmalloc (sizeof (struct bar_progress));
472 memset (bp, 0, sizeof (*bp));
474 /* In theory, our callers should take care of this pathological
475 case, but it can sometimes happen. */
479 bp->initial_length = initial;
480 bp->total_length = total;
482 /* - 1 because we don't want to use the last screen column. */
483 bp->width = screen_width - 1;
484 /* + 1 for the terminating zero. */
485 bp->buffer = xmalloc (bp->width + 1);
487 logputs (LOG_VERBOSE, "\n");
489 create_image (bp, 0);
490 display_image (bp->buffer);
496 bar_update (void *progress, long howmuch, long dltime)
498 struct bar_progress *bp = progress;
499 struct bar_progress_hist *hist = &bp->hist;
500 int force_screen_update = 0;
501 long delta_time = dltime - hist->previous_time;
503 bp->count += howmuch;
504 if (bp->total_length > 0
505 && bp->count + bp->initial_length > bp->total_length)
506 /* We could be downloading more than total_length, e.g. when the
507 server sends an incorrect Content-Length header. In that case,
508 adjust bp->total_length to the new reality, so that the code in
509 create_image() that depends on total size being smaller or
510 equal to the expected size doesn't abort. */
511 bp->total_length = bp->initial_length + bp->count;
513 /* This code attempts to determine the current download speed. We
514 measure the speed over the interval of approximately three
515 seconds, in subintervals no smaller than 0.1s. In other words,
516 we maintain and use the history of 30 most recent reads, where a
517 "read" consists of one or more network reads, up until the point
518 where a subinterval is filled. */
520 if (hist->times[hist->pos]
521 >= DLSPEED_HISTORY_MAX_INTERVAL / DLSPEED_HISTORY_SIZE)
523 /* The subinterval at POS has been used up. Move on to the next
525 if (++hist->pos == DLSPEED_HISTORY_SIZE)
528 /* Invalidate old data (from the previous cycle) at this
530 hist->summed_times -= hist->times[hist->pos];
531 hist->summed_bytes -= hist->bytes[hist->pos];
532 hist->times[hist->pos] = delta_time;
533 hist->bytes[hist->pos] = howmuch;
537 /* Increment the data at POS. */
538 hist->times[hist->pos] += delta_time;
539 hist->bytes[hist->pos] += howmuch;
542 hist->summed_times += delta_time;
543 hist->summed_bytes += howmuch;
544 hist->previous_time = dltime;
547 /* Sledgehammer check that summed_times and summed_bytes are
551 long sumt = 0, sumb = 0;
552 for (i = 0; i < DLSPEED_HISTORY_SIZE; i++)
554 sumt += hist->times[i];
555 sumb += hist->bytes[i];
557 assert (sumt == hist->summed_times);
558 assert (sumb == hist->summed_bytes);
562 if (screen_width - 1 != bp->width)
564 bp->width = screen_width - 1;
565 bp->buffer = xrealloc (bp->buffer, bp->width + 1);
566 force_screen_update = 1;
569 if (dltime - bp->last_screen_update < 200 && !force_screen_update)
570 /* Don't update more often than five times per second. */
573 create_image (bp, dltime);
574 display_image (bp->buffer);
575 bp->last_screen_update = dltime;
579 bar_finish (void *progress, long dltime)
581 struct bar_progress *bp = progress;
583 if (bp->total_length > 0
584 && bp->count + bp->initial_length > bp->total_length)
585 /* See bar_update() for explanation. */
586 bp->total_length = bp->initial_length + bp->count;
588 create_image (bp, dltime);
589 display_image (bp->buffer);
591 logputs (LOG_VERBOSE, "\n\n");
597 #define APPEND_LITERAL(s) do { \
598 memcpy (p, s, sizeof (s) - 1); \
599 p += sizeof (s) - 1; \
603 # define MAX(a, b) ((a) >= (b) ? (a) : (b))
607 create_image (struct bar_progress *bp, long dl_total_time)
609 char *p = bp->buffer;
610 long size = bp->initial_length + bp->count;
612 char *size_legible = legible (size);
613 int size_legible_len = strlen (size_legible);
615 struct bar_progress_hist *hist = &bp->hist;
617 /* The progress bar should look like this:
618 xx% [=======> ] nn,nnn 12.34K/s ETA 00:00
620 Calculate the geometry. The idea is to assign as much room as
621 possible to the progress bar. The other idea is to never let
622 things "jitter", i.e. pad elements that vary in size so that
623 their variance does not affect the placement of other elements.
624 It would be especially bad for the progress bar to be resized
627 "xx% " or "100%" - percentage - 4 chars
628 "[]" - progress bar decorations - 2 chars
629 " nnn,nnn,nnn" - downloaded bytes - 12 chars or very rarely more
630 " 1012.56K/s" - dl rate - 11 chars
631 " ETA xx:xx:xx" - ETA - 13 chars
633 "=====>..." - progress bar - the rest
635 int dlbytes_size = 1 + MAX (size_legible_len, 11);
636 int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);
638 if (progress_size < 5)
642 if (bp->total_length > 0)
644 int percentage = (int)(100.0 * size / bp->total_length);
646 assert (percentage <= 100);
648 if (percentage < 100)
649 sprintf (p, "%2d%% ", percentage);
655 APPEND_LITERAL (" ");
657 /* The progress bar: "[====> ]" */
658 if (progress_size && bp->total_length > 0)
660 double fraction = (double)size / bp->total_length;
661 int dlsz = (int)(fraction * progress_size);
664 assert (dlsz <= progress_size);
671 /* Draw dlsz-1 '=' chars and one arrow char. */
677 while (p - begin < progress_size)
682 else if (progress_size)
684 /* If we can't draw a real progress bar, then at least show
685 *something* to the user. */
686 int ind = bp->tick % (progress_size * 2 - 6);
689 /* Make the star move in two directions. */
690 if (ind < progress_size - 2)
693 pos = progress_size - (ind - progress_size + 5);
696 for (i = 0; i < progress_size; i++)
698 if (i == pos - 1) *p++ = '<';
699 else if (i == pos ) *p++ = '=';
700 else if (i == pos + 1) *p++ = '>';
710 sprintf (p, " %-11s", legible (size));
714 if (hist->summed_times && hist->summed_bytes)
716 static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
719 dlrate = calc_rate (hist->summed_bytes, hist->summed_times, &units);
720 sprintf (p, " %7.2f%s", dlrate, short_units[units]);
724 APPEND_LITERAL (" --.--K/s");
726 /* " ETA xx:xx:xx" */
727 if (bp->total_length > 0 && dl_total_time > 3000)
730 int eta_hrs, eta_min, eta_sec;
732 /* Don't change the value of ETA more than approximately once
733 per second; doing so would cause flashing without providing
734 any value to the user. */
735 if (dl_total_time - bp->last_eta_time < 900
736 && bp->last_eta_value != 0)
737 eta = bp->last_eta_value;
740 /* Calculate ETA using the average download speed to predict
741 the future speed. If you want to use the current speed
742 instead, replace dl_total_time with hist->summed_times
743 and bp->count with hist->summed_bytes. I found that
744 doing that results in a very jerky and ultimately
746 double time_sofar = (double)dl_total_time / 1000;
747 long bytes_remaining = bp->total_length - size;
748 eta = (long) (time_sofar * bytes_remaining / bp->count);
749 bp->last_eta_value = eta;
750 bp->last_eta_time = dl_total_time;
753 eta_hrs = eta / 3600, eta %= 3600;
754 eta_min = eta / 60, eta %= 60;
762 /* Hours not printed: pad with three spaces. */
763 APPEND_LITERAL (" ");
764 sprintf (p, " ETA %02d:%02d", eta_min, eta_sec);
769 /* Hours printed with one digit: pad with one space. */
771 sprintf (p, " ETA %d:%02d:%02d", eta_hrs, eta_min, eta_sec);
775 else if (bp->total_length > 0)
778 APPEND_LITERAL (" ");
781 assert (p - bp->buffer <= bp->width);
783 while (p < bp->buffer + bp->width)
788 /* Print the contents of the buffer as a one-line ASCII "image" so
789 that it can be overwritten next time. */
792 display_image (char *buf)
794 int old = log_set_save_context (0);
795 logputs (LOG_VERBOSE, "\r");
796 logputs (LOG_VERBOSE, buf);
797 log_set_save_context (old);
801 bar_set_params (const char *params)
806 && 0 == strcmp (params, "force"))
807 current_impl_locked = 1;
811 || !isatty (fileno (stderr))
816 && !current_impl_locked)
818 /* We're not printing to a TTY, so revert to the fallback
819 display. #### We're recursively calling
820 set_progress_implementation here, which is slightly kludgy.
821 It would be nicer if we provided that function a return value
822 indicating a failure of some sort. */
823 set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
827 sw = determine_screen_width ();
828 if (sw && sw >= MINIMUM_SCREEN_WIDTH)
834 progress_handle_sigwinch (int sig)
836 int sw = determine_screen_width ();
837 if (sw && sw >= MINIMUM_SCREEN_WIDTH)
839 signal (SIGWINCH, progress_handle_sigwinch);