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, double));
56 void (*finish) PARAMS ((void *, double));
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, double));
64 static void dot_finish PARAMS ((void *, double));
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, double));
69 static void bar_finish PARAMS ((void *, double));
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, double 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, double 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 double 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, double 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, double 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, double 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 download speed history ring. */
417 #define DLSPEED_HISTORY_SIZE 30
419 /* The minimum time length of a history sample. By default, each
420 sample is at least 100ms long, which means that, over the course of
421 30 samples, "current" download speed spans at least 3s into the
423 #define DLSPEED_SAMPLE_MIN 100
425 struct bar_progress {
426 long initial_length; /* how many bytes have been downloaded
428 long total_length; /* expected total byte count when the
430 long count; /* bytes downloaded so far */
432 double last_screen_update; /* time of the last screen update,
433 measured since the beginning of
436 int width; /* screen width we're using at the
437 time the progress gauge was
438 created. this is different from
439 the screen_width global variable in
440 that the latter can be changed by a
442 char *buffer; /* buffer where the bar "image" is
444 int tick; /* counter used for drawing the
445 progress bar where the total size
448 /* The following variables (kept in a struct for namespace reasons)
449 keep track of recent download speeds. See bar_update() for
451 struct bar_progress_hist {
453 long times[DLSPEED_HISTORY_SIZE];
454 long bytes[DLSPEED_HISTORY_SIZE];
456 /* The sum of times and bytes respectively, maintained for
462 double recent_start; /* timestamp of beginning of current
464 long recent_bytes; /* bytes downloaded so far. */
466 /* create_image() uses these to make sure that ETA information
468 double last_eta_time; /* time of the last update to download
469 speed and ETA, measured since the
470 beginning of download. */
474 static void create_image PARAMS ((struct bar_progress *, double));
475 static void display_image PARAMS ((char *));
478 bar_create (long initial, long total)
480 struct bar_progress *bp = xmalloc (sizeof (struct bar_progress));
482 memset (bp, 0, sizeof (*bp));
484 /* In theory, our callers should take care of this pathological
485 case, but it can sometimes happen. */
489 bp->initial_length = initial;
490 bp->total_length = total;
492 /* - 1 because we don't want to use the last screen column. */
493 bp->width = screen_width - 1;
494 /* + 1 for the terminating zero. */
495 bp->buffer = xmalloc (bp->width + 1);
497 logputs (LOG_VERBOSE, "\n");
499 create_image (bp, 0);
500 display_image (bp->buffer);
505 static void update_speed_ring PARAMS ((struct bar_progress *, long, double));
508 bar_update (void *progress, long howmuch, double dltime)
510 struct bar_progress *bp = progress;
511 int force_screen_update = 0;
513 bp->count += howmuch;
514 if (bp->total_length > 0
515 && bp->count + bp->initial_length > bp->total_length)
516 /* We could be downloading more than total_length, e.g. when the
517 server sends an incorrect Content-Length header. In that case,
518 adjust bp->total_length to the new reality, so that the code in
519 create_image() that depends on total size being smaller or
520 equal to the expected size doesn't abort. */
521 bp->total_length = bp->initial_length + bp->count;
523 update_speed_ring (bp, howmuch, dltime);
525 if (screen_width - 1 != bp->width)
527 bp->width = screen_width - 1;
528 bp->buffer = xrealloc (bp->buffer, bp->width + 1);
529 force_screen_update = 1;
532 if (dltime - bp->last_screen_update < 200 && !force_screen_update)
533 /* Don't update more often than five times per second. */
536 create_image (bp, dltime);
537 display_image (bp->buffer);
538 bp->last_screen_update = dltime;
542 bar_finish (void *progress, double dltime)
544 struct bar_progress *bp = progress;
546 if (bp->total_length > 0
547 && bp->count + bp->initial_length > bp->total_length)
548 /* See bar_update() for explanation. */
549 bp->total_length = bp->initial_length + bp->count;
551 create_image (bp, dltime);
552 display_image (bp->buffer);
554 logputs (LOG_VERBOSE, "\n\n");
560 /* This code attempts to maintain the notion of a "current" download
561 speed, over the course of no less than 3s. (Shorter intervals
562 produce very erratic results.)
564 To do so, it samples the speed in 0.1s intervals and stores the
565 recorded samples in a FIFO history ring. The ring stores no more
566 than 30 intervals, hence the history covers the period of at least
567 three seconds and at most 30 reads into the past. This method
568 should produce good results for both very fast and very slow
571 The idea is that for fast downloads, we get the speed over exactly
572 the last three seconds. For slow downloads (where a network read
573 takes more than 0.1s to complete), we get the speed over a larger
574 time period, as large as it takes to complete thirty reads. This
575 is good because slow downloads tend to fluctuate more and a
576 3-second average would be very erratic. */
579 update_speed_ring (struct bar_progress *bp, long howmuch, double dltime)
581 struct bar_progress_hist *hist = &bp->hist;
582 double recent_age = dltime - bp->recent_start;
584 /* Update the download count. */
585 bp->recent_bytes += howmuch;
587 /* For very small time intervals, we return after having updated the
588 "recent" download count. When its age reaches or exceeds minimum
589 sample time, it will be recorded in the history ring. */
590 if (recent_age < DLSPEED_SAMPLE_MIN)
593 /* Store "recent" bytes and download time to history ring at the
596 /* To correctly maintain the totals, first invalidate existing data
597 (least recent in time) at this position. */
598 hist->total_time -= hist->times[hist->pos];
599 hist->total_bytes -= hist->bytes[hist->pos];
601 /* Now store the new data and update the totals. */
602 hist->times[hist->pos] = recent_age;
603 hist->bytes[hist->pos] = bp->recent_bytes;
604 hist->total_time += recent_age;
605 hist->total_bytes += bp->recent_bytes;
607 /* Start a new "recent" period. */
608 bp->recent_start = dltime;
609 bp->recent_bytes = 0;
611 /* Advance the current ring position. */
612 if (++hist->pos == DLSPEED_HISTORY_SIZE)
616 /* Sledgehammer check to verify that the totals are accurate. */
619 double sumt = 0, sumb = 0;
620 for (i = 0; i < DLSPEED_HISTORY_SIZE; i++)
622 sumt += hist->times[i];
623 sumb += hist->bytes[i];
625 assert (sumt == hist->total_time);
626 assert (sumb == hist->total_bytes);
631 #define APPEND_LITERAL(s) do { \
632 memcpy (p, s, sizeof (s) - 1); \
633 p += sizeof (s) - 1; \
637 # define MAX(a, b) ((a) >= (b) ? (a) : (b))
641 create_image (struct bar_progress *bp, double dl_total_time)
643 char *p = bp->buffer;
644 long size = bp->initial_length + bp->count;
646 char *size_legible = legible (size);
647 int size_legible_len = strlen (size_legible);
649 struct bar_progress_hist *hist = &bp->hist;
651 /* The progress bar should look like this:
652 xx% [=======> ] nn,nnn 12.34K/s ETA 00:00
654 Calculate the geometry. The idea is to assign as much room as
655 possible to the progress bar. The other idea is to never let
656 things "jitter", i.e. pad elements that vary in size so that
657 their variance does not affect the placement of other elements.
658 It would be especially bad for the progress bar to be resized
661 "xx% " or "100%" - percentage - 4 chars
662 "[]" - progress bar decorations - 2 chars
663 " nnn,nnn,nnn" - downloaded bytes - 12 chars or very rarely more
664 " 1012.56K/s" - dl rate - 11 chars
665 " ETA xx:xx:xx" - ETA - 13 chars
667 "=====>..." - progress bar - the rest
669 int dlbytes_size = 1 + MAX (size_legible_len, 11);
670 int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);
672 if (progress_size < 5)
676 if (bp->total_length > 0)
678 int percentage = (int)(100.0 * size / bp->total_length);
680 assert (percentage <= 100);
682 if (percentage < 100)
683 sprintf (p, "%2d%% ", percentage);
689 APPEND_LITERAL (" ");
691 /* The progress bar: "[====> ]" */
692 if (progress_size && bp->total_length > 0)
694 double fraction = (double)size / bp->total_length;
695 int dlsz = (int)(fraction * progress_size);
698 assert (dlsz <= progress_size);
705 /* Draw dlsz-1 '=' chars and one arrow char. */
711 while (p - begin < progress_size)
716 else if (progress_size)
718 /* If we can't draw a real progress bar, then at least show
719 *something* to the user. */
720 int ind = bp->tick % (progress_size * 2 - 6);
723 /* Make the star move in two directions. */
724 if (ind < progress_size - 2)
727 pos = progress_size - (ind - progress_size + 5);
730 for (i = 0; i < progress_size; i++)
732 if (i == pos - 1) *p++ = '<';
733 else if (i == pos ) *p++ = '=';
734 else if (i == pos + 1) *p++ = '>';
744 sprintf (p, " %-11s", legible (size));
748 if (hist->total_time && hist->total_bytes)
750 static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
752 long bytes = hist->total_bytes + bp->recent_bytes;
753 double tm = hist->total_time + dl_total_time - bp->recent_start;
754 double dlrate = calc_rate (bytes, tm, &units);
755 sprintf (p, " %7.2f%s", dlrate, short_units[units]);
759 APPEND_LITERAL (" --.--K/s");
761 /* " ETA xx:xx:xx" */
762 if (bp->total_length > 0 && dl_total_time > 3000)
765 int eta_hrs, eta_min, eta_sec;
767 /* Don't change the value of ETA more than approximately once
768 per second; doing so would cause flashing without providing
769 any value to the user. */
770 if (dl_total_time - bp->last_eta_time < 900
771 && bp->last_eta_value != 0)
772 eta = bp->last_eta_value;
775 /* Calculate ETA using the average download speed to predict
776 the future speed. If you want to use a speed averaged
777 over a more recent period, replace dl_total_time with
778 hist->total_time and bp->count with hist->total_bytes.
779 I found that doing that results in a very jerky and
780 ultimately unreliable ETA. */
781 double time_sofar = (double)dl_total_time / 1000;
782 long bytes_remaining = bp->total_length - size;
783 eta = (long) (time_sofar * bytes_remaining / bp->count);
784 bp->last_eta_value = eta;
785 bp->last_eta_time = dl_total_time;
788 eta_hrs = eta / 3600, eta %= 3600;
789 eta_min = eta / 60, eta %= 60;
797 /* Hours not printed: pad with three spaces. */
798 APPEND_LITERAL (" ");
799 sprintf (p, " ETA %02d:%02d", eta_min, eta_sec);
804 /* Hours printed with one digit: pad with one space. */
806 sprintf (p, " ETA %d:%02d:%02d", eta_hrs, eta_min, eta_sec);
810 else if (bp->total_length > 0)
813 APPEND_LITERAL (" ");
816 assert (p - bp->buffer <= bp->width);
818 while (p < bp->buffer + bp->width)
823 /* Print the contents of the buffer as a one-line ASCII "image" so
824 that it can be overwritten next time. */
827 display_image (char *buf)
829 int old = log_set_save_context (0);
830 logputs (LOG_VERBOSE, "\r");
831 logputs (LOG_VERBOSE, buf);
832 log_set_save_context (old);
836 bar_set_params (const char *params)
839 char *term = getenv ("TERM");
842 && 0 == strcmp (params, "force"))
843 current_impl_locked = 1;
847 /* The progress bar doesn't make sense if the output is not a
848 TTY -- when logging to file, it is better to review the
850 || !isatty (fileno (stderr))
854 /* Normally we don't depend on terminal type because the
855 progress bar only uses ^M to move the cursor to the
856 beginning of line, which works even on dumb terminals. But
857 Jamie Zawinski reports that ^M and ^H tricks don't work in
858 Emacs shell buffers, and only make a mess. */
859 || (term && 0 == strcmp (term, "emacs"))
861 && !current_impl_locked)
863 /* We're not printing to a TTY, so revert to the fallback
864 display. #### We're recursively calling
865 set_progress_implementation here, which is slightly kludgy.
866 It would be nicer if we provided that function a return value
867 indicating a failure of some sort. */
868 set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
872 sw = determine_screen_width ();
873 if (sw && sw >= MINIMUM_SCREEN_WIDTH)
879 progress_handle_sigwinch (int sig)
881 int sw = determine_screen_width ();
882 if (sw && sw >= MINIMUM_SCREEN_WIDTH)
884 signal (SIGWINCH, progress_handle_sigwinch);