2 Copyright (C) 2001 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. */
28 #endif /* HAVE_STRING_H */
39 struct progress_implementation {
41 void *(*create) (long, long);
42 void (*update) (void *, long, long);
43 void (*finish) (void *, long);
44 void (*set_params) (const char *);
47 /* Necessary forward declarations. */
49 static void *dot_create PARAMS ((long, long));
50 static void dot_update PARAMS ((void *, long, long));
51 static void dot_finish PARAMS ((void *, long));
52 static void dot_set_params PARAMS ((const char *));
54 static void *bar_create PARAMS ((long, long));
55 static void bar_update PARAMS ((void *, long, long));
56 static void bar_finish PARAMS ((void *, long));
57 static void bar_set_params PARAMS ((const char *));
59 static struct progress_implementation implementations[] = {
60 { "dot", dot_create, dot_update, dot_finish, dot_set_params },
61 { "bar", bar_create, bar_update, bar_finish, bar_set_params }
63 static struct progress_implementation *current_impl;
65 /* Default progress implementation should be something that works
66 under all display types. If you put something other than "dot"
67 here, remember that bar_set_params tries to switch to this if we're
68 not running on a TTY. So changing this to "bar" could cause
71 #define DEFAULT_PROGRESS_IMPLEMENTATION "dot"
73 /* Return non-zero if NAME names a valid progress bar implementation.
74 The characters after the first : will be ignored. */
77 valid_progress_implementation_p (const char *name)
80 struct progress_implementation *pi = implementations;
81 char *colon = strchr (name, ':');
82 int namelen = colon ? colon - name : strlen (name);
84 for (i = 0; i < ARRAY_SIZE (implementations); i++, pi++)
85 if (!strncmp (pi->name, name, namelen))
90 /* Set the progress implementation to NAME. */
93 set_progress_implementation (const char *name)
96 struct progress_implementation *pi = implementations;
100 name = DEFAULT_PROGRESS_IMPLEMENTATION;
102 colon = strchr (name, ':');
103 namelen = colon ? colon - name : strlen (name);
105 for (i = 0; i < ARRAY_SIZE (implementations); i++, pi++)
106 if (!strncmp (pi->name, name, namelen))
111 /* We call pi->set_params even if colon is NULL because we
112 want to give the implementation a chance to set up some
113 things it needs to run. */
117 pi->set_params (colon);
123 /* Create a progress gauge. INITIAL is the number of bytes the
124 download starts from (zero if the download starts from scratch).
125 TOTAL is the expected total number of bytes in this download. If
126 TOTAL is zero, it means that the download size is not known in
130 progress_create (long initial, long total)
132 return current_impl->create (initial, total);
135 /* Inform the progress gauge of newly received bytes. DLTIME is the
136 time in milliseconds since the beginning of the download. */
139 progress_update (void *progress, long howmuch, long dltime)
141 current_impl->update (progress, howmuch, dltime);
144 /* Tell the progress gauge to clean up. Calling this will free the
145 PROGRESS object, the further use of which is not allowed. */
148 progress_finish (void *progress, long dltime)
150 current_impl->finish (progress, dltime);
155 struct dot_progress {
156 long initial_length; /* how many bytes have been downloaded
158 long total_length; /* expected total byte count when the
163 int rows; /* number of rows printed so far */
164 int dots; /* number of dots printed in this row */
165 long last_timer_value;
168 /* Dot-progress backend for progress_create. */
171 dot_create (long initial, long total)
173 struct dot_progress *dp = xmalloc (sizeof (struct dot_progress));
175 memset (dp, 0, sizeof (*dp));
177 dp->initial_length = initial;
178 dp->total_length = total;
180 if (dp->initial_length)
182 int dot_bytes = opt.dot_bytes;
183 long row_bytes = opt.dot_bytes * opt.dots_in_line;
185 int remainder = (int) (dp->initial_length % row_bytes);
186 long skipped = dp->initial_length - remainder;
190 int skipped_k = (int) (skipped / 1024); /* skipped amount in K */
191 int skipped_k_len = numdigit (skipped_k);
192 if (skipped_k_len < 5)
195 /* Align the [ skipping ... ] line with the dots. To do
196 that, insert the number of spaces equal to the number of
197 digits in the skipped amount in K. */
198 logprintf (LOG_VERBOSE, _("\n%*s[ skipping %dK ]"),
199 2 + skipped_k_len, "", skipped_k);
202 logprintf (LOG_VERBOSE, "\n%5ldK", skipped / 1024);
203 for (; remainder >= dot_bytes; remainder -= dot_bytes)
205 if (dp->dots % opt.dot_spacing == 0)
206 logputs (LOG_VERBOSE, " ");
207 logputs (LOG_VERBOSE, ",");
210 assert (dp->dots < opt.dots_in_line);
212 dp->accumulated = remainder;
213 dp->rows = skipped / row_bytes;
220 print_percentage (long bytes, long expected)
222 int percentage = (int)(100.0 * bytes / expected);
223 logprintf (LOG_VERBOSE, "%3d%%", percentage);
227 print_download_speed (struct dot_progress *dp, long bytes, long dltime)
229 logprintf (LOG_VERBOSE, " %s",
230 retr_rate (bytes, dltime - dp->last_timer_value, 1));
231 dp->last_timer_value = dltime;
234 /* Dot-progress backend for progress_update. */
237 dot_update (void *progress, long howmuch, long dltime)
239 struct dot_progress *dp = progress;
240 int dot_bytes = opt.dot_bytes;
241 long row_bytes = opt.dot_bytes * opt.dots_in_line;
245 dp->accumulated += howmuch;
246 for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
249 logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
251 if (dp->dots % opt.dot_spacing == 0)
252 logputs (LOG_VERBOSE, " ");
253 logputs (LOG_VERBOSE, ".");
256 if (dp->dots >= opt.dots_in_line)
258 long row_qty = row_bytes;
259 if (dp->rows == dp->initial_length / row_bytes)
260 row_qty -= dp->initial_length % row_bytes;
265 if (dp->total_length)
266 print_percentage (dp->rows * row_bytes, dp->total_length);
267 print_download_speed (dp, row_qty, dltime);
274 /* Dot-progress backend for progress_finish. */
277 dot_finish (void *progress, long dltime)
279 struct dot_progress *dp = progress;
280 int dot_bytes = opt.dot_bytes;
281 long row_bytes = opt.dot_bytes * opt.dots_in_line;
286 for (i = dp->dots; i < opt.dots_in_line; i++)
288 if (i % opt.dot_spacing == 0)
289 logputs (LOG_VERBOSE, " ");
290 logputs (LOG_VERBOSE, " ");
292 if (dp->total_length)
294 print_percentage (dp->rows * row_bytes
295 + dp->dots * dot_bytes
301 long row_qty = dp->dots * dot_bytes + dp->accumulated;
302 if (dp->rows == dp->initial_length / row_bytes)
303 row_qty -= dp->initial_length % row_bytes;
304 print_download_speed (dp, row_qty, dltime);
307 logputs (LOG_VERBOSE, "\n\n");
313 /* This function interprets the progress "parameters". For example,
314 if Wget is invoked with --progress=bar:mega, it will set the
315 "dot-style" to "mega". Valid styles are default, binary, mega, and
319 dot_set_params (const char *params)
324 /* We use this to set the retrieval style. */
325 if (!strcasecmp (params, "default"))
327 /* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
329 opt.dot_bytes = 1024;
330 opt.dot_spacing = 10;
331 opt.dots_in_line = 50;
333 else if (!strcasecmp (params, "binary"))
335 /* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
337 opt.dot_bytes = 8192;
338 opt.dot_spacing = 16;
339 opt.dots_in_line = 48;
341 else if (!strcasecmp (params, "mega"))
343 /* "Mega" retrieval, for retrieving very long files; each dot is
344 64K, 8 dots in a cluster, 6 clusters (3M) in a line. */
345 opt.dot_bytes = 65536L;
347 opt.dots_in_line = 48;
349 else if (!strcasecmp (params, "giga"))
351 /* "Giga" retrieval, for retrieving very very *very* long files;
352 each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
354 opt.dot_bytes = (1L << 20);
356 opt.dots_in_line = 32;
360 _("Invalid dot style specification `%s'; leaving unchanged.\n"),
364 /* "Thermometer" (bar) progress. */
366 /* Assumed screen width if we can't find the real value. */
367 #define DEFAULT_SCREEN_WIDTH 80
369 /* Minimum screen width we'll try to work with. If this is too small,
370 create_image will overflow the buffer. */
371 #define MINIMUM_SCREEN_WIDTH 45
373 static int screen_width = DEFAULT_SCREEN_WIDTH;
375 struct bar_progress {
376 long initial_length; /* how many bytes have been downloaded
378 long total_length; /* expected total byte count when the
380 long count; /* bytes downloaded so far */
382 long last_update; /* time of the last screen update. */
384 int width; /* screen width at the time the
385 progress gauge was created. */
386 char *buffer; /* buffer where the bar "image" is
392 static void create_image PARAMS ((struct bar_progress *, long));
393 static void display_image PARAMS ((char *));
396 bar_create (long initial, long total)
398 struct bar_progress *bp = xmalloc (sizeof (struct bar_progress));
400 memset (bp, 0, sizeof (*bp));
402 bp->initial_length = initial;
403 bp->total_length = total;
404 bp->width = screen_width;
405 bp->buffer = xmalloc (bp->width + 1);
407 logputs (LOG_VERBOSE, "\n");
409 create_image (bp, 0);
410 display_image (bp->buffer);
416 bar_update (void *progress, long howmuch, long dltime)
418 struct bar_progress *bp = progress;
419 int force_update = 0;
421 bp->count += howmuch;
422 if (bp->total_length > 0
423 && bp->count + bp->initial_length > bp->total_length)
424 /* We could be downloading more than total_length, e.g. when the
425 server sends an incorrect Content-Length header. In that case,
426 adjust bp->total_length to the new reality, so that the code in
427 create_image() that depends on total size being smaller or
428 equal to the expected size doesn't abort. */
429 bp->total_length = bp->count + bp->initial_length;
431 if (screen_width != bp->width)
433 bp->width = screen_width;
434 bp->buffer = xrealloc (bp->buffer, bp->width + 1);
437 if (dltime - bp->last_update < 200 && !force_update)
438 /* Don't update more often than every half a second. */
441 bp->last_update = dltime;
443 create_image (bp, dltime);
444 display_image (bp->buffer);
448 bar_finish (void *progress, long dltime)
450 struct bar_progress *bp = progress;
453 /* If the download was faster than the granularity of the timer,
454 fake some output so that we don't get the ugly "----.--" rate
455 at the download finish. */
458 create_image (bp, dltime);
459 display_image (bp->buffer);
461 logputs (LOG_VERBOSE, "\n\n");
468 create_image (struct bar_progress *bp, long dltime)
470 char *p = bp->buffer;
471 long size = bp->initial_length + bp->count;
473 /* The progress bar should look like this:
474 xx% [=======> ] nn.nnn rrK/s ETA 00:00
476 Calculate its geometry:
478 "xx% " or "100%" - percentage - 4 chars exactly
479 "[]" - progress bar decorations - 2 chars exactly
480 " n,nnn,nnn,nnn" - downloaded bytes - 14 or less chars
481 " 1012.56K/s" - dl rate - 11 chars exactly
482 " ETA xx:xx:xx" - ETA - 13 or less chars
484 "=====>..." - progress bar content - the rest
486 int progress_size = screen_width - (4 + 2 + 14 + 11 + 13);
488 if (progress_size < 5)
492 if (bp->total_length > 0)
494 int percentage = (int)(100.0 * size / bp->total_length);
496 assert (percentage <= 100);
498 if (percentage < 100)
499 sprintf (p, "%2d%% ", percentage);
512 /* The progress bar: "[====> ]" */
513 if (progress_size && bp->total_length > 0)
515 double fraction = (double)size / bp->total_length;
516 int dlsz = (int)(fraction * progress_size);
519 assert (dlsz <= progress_size);
526 /* Draw dlsz-1 '=' chars and one arrow char. */
532 while (p - begin < progress_size)
537 else if (progress_size)
539 /* If we can't draw a real progress bar, then at least show
540 *something* to the user. */
541 int ind = bp->tick % (progress_size * 2 - 6);
544 /* Make the star move in two directions. */
545 if (ind < progress_size - 2)
548 pos = progress_size - (ind - progress_size + 5);
551 for (i = 0; i < progress_size; i++)
553 if (i == pos - 1) *p++ = '<';
554 else if (i == pos ) *p++ = '=';
555 else if (i == pos + 1) *p++ = '>';
565 /* If there are 7 or less digits (9 because of "legible" comas),
566 print the number in constant space. This will prevent the rest
567 of the line jerking at the beginning of download, but without
568 assigning maximum width in all cases. */
569 sprintf (p, " %9s", legible (size));
573 if (dltime && bp->count)
575 static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
577 double dlrate = calc_rate (bp->count, dltime, &units);
578 sprintf (p, " %7.2f%s", dlrate, short_units[units]);
583 strcpy (p, " --.--K/s");
587 /* " ETA xx:xx:xx" */
588 if (bp->total_length > 0 && bp->count > 0)
590 int eta, eta_hrs, eta_min, eta_sec;
591 double tm_sofar = (double)dltime / 1000;
592 long bytes_remaining = bp->total_length - size;
594 eta = (int) (tm_sofar * bytes_remaining / bp->count);
596 eta_hrs = eta / 3600, eta %= 3600;
597 eta_min = eta / 60, eta %= 60;
600 /*printf ("\neta: %d, %d %d %d\n", eta, eta_hrs, eta_min, eta_sec);*/
601 /*printf ("\n%ld %f %ld %ld\n", dltime, tm_sofar, bytes_remaining, bp->count);*/
610 /* Bogus value, for whatever reason. We must avoid overflow. */
611 sprintf (p, "--:--");
612 else if (eta_hrs > 0)
613 sprintf (p, "%d:%02d:%02d", eta_hrs, eta_min, eta_sec);
615 sprintf (p, "%02d:%02d", eta_min, eta_sec);
618 else if (bp->total_length > 0)
620 strcpy (p, " ETA --:--");
624 assert (p - bp->buffer <= screen_width);
626 while (p < bp->buffer + screen_width)
631 /* Print the contents of the buffer as a one-line ASCII "image" so
632 that it can be overwritten next time. */
635 display_image (char *buf)
637 char *del_buf = alloca (screen_width + 1);
638 memset (del_buf, '\b', screen_width);
639 del_buf[screen_width] = '\0';
640 logputs (LOG_VERBOSE, del_buf);
641 logputs (LOG_VERBOSE, buf);
645 bar_set_params (const char *params)
651 || !isatty (fileno (stderr))
657 && 0 == strcmp (params, "force")))
659 /* We're not printing to a TTY, so revert to the fallback
660 display. #### We're recursively calling
661 set_progress_implementation here, which is slightly kludgy.
662 It would be nicer if that function could resolve this problem
664 set_progress_implementation (NULL);
668 sw = determine_screen_width ();
669 if (sw && sw >= MINIMUM_SCREEN_WIDTH)
674 progress_handle_sigwinch (int sig)
676 int sw = determine_screen_width ();
677 if (sw && sw >= MINIMUM_SCREEN_WIDTH)