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 */
36 struct progress_implementation {
38 void *(*create) (long, long);
39 void (*update) (void *, long);
40 void (*finish) (void *);
41 void (*set_params) (const char *);
44 /* Necessary forward declarations. */
46 static void *dp_create PARAMS ((long, long));
47 static void dp_update PARAMS ((void *, long));
48 static void dp_finish PARAMS ((void *));
49 static void dp_set_params PARAMS ((const char *));
51 static void *bar_create PARAMS ((long, long));
52 static void bar_update PARAMS ((void *, long));
53 static void bar_finish PARAMS ((void *));
54 static void bar_set_params PARAMS ((const char *));
56 static struct progress_implementation implementations[] = {
57 { "dot", dp_create, dp_update, dp_finish, dp_set_params },
58 { "bar", bar_create, bar_update, bar_finish, bar_set_params }
60 static struct progress_implementation *current_impl;
62 /* Return non-zero if NAME names a valid progress bar implementation.
63 The characters after the first : will be ignored. */
66 valid_progress_implementation_p (const char *name)
69 struct progress_implementation *pi = implementations;
70 char *colon = strchr (name, ':');
71 int namelen = colon ? colon - name : strlen (name);
73 for (i = 0; i < ARRAY_SIZE (implementations); i++, pi++)
74 if (!strncmp (pi->name, name, namelen))
79 /* Set the progress implementation to NAME. */
82 set_progress_implementation (const char *name)
85 struct progress_implementation *pi = implementations;
86 char *colon = strchr (name, ':');
87 int namelen = colon ? colon - name : strlen (name);
89 for (i = 0; i < ARRAY_SIZE (implementations); i++, pi++)
90 if (!strncmp (pi->name, name, namelen))
95 /* We call pi->set_params even if colon is NULL because we
96 want to give the implementation a chance to set up some
97 things it needs to run. */
101 pi->set_params (colon);
107 /* Create a progress gauge. INITIAL is the number of bytes the
108 download starts from (zero if the download starts from scratch).
109 TOTAL is the expected total number of bytes in this download. If
110 TOTAL is zero, it means that the download size is not known in
114 progress_create (long initial, long total)
116 return current_impl->create (initial, total);
119 /* Inform the progress gauge of newly received bytes. */
122 progress_update (void *progress, long howmuch)
124 current_impl->update (progress, howmuch);
127 /* Tell the progress gauge to clean up. Calling this will free the
128 PROGRESS object, the further use of which is not allowed. */
131 progress_finish (void *progress)
133 current_impl->finish (progress);
138 struct dot_progress {
139 long initial_length; /* how many bytes have been downloaded
141 long total_length; /* expected total byte count when the
146 int rows; /* number of rows printed so far */
147 int dots; /* number of dots printed in this row */
149 struct wget_timer *timer; /* timer used to measure per-row
151 long last_timer_value;
154 /* Dot-progress backend for progress_create. */
157 dp_create (long initial, long total)
159 struct dot_progress *dp = xmalloc (sizeof (struct dot_progress));
161 memset (dp, 0, sizeof (*dp));
163 dp->initial_length = initial;
164 dp->total_length = total;
165 dp->timer = wtimer_new ();
167 if (dp->initial_length)
169 int dot_bytes = opt.dot_bytes;
170 long row_bytes = opt.dot_bytes * opt.dots_in_line;
172 int remainder = (int) (dp->initial_length % row_bytes);
173 long skipped = dp->initial_length - remainder;
177 logputs (LOG_VERBOSE, "\n "); /* leave spacing untranslated */
178 logprintf (LOG_VERBOSE, _("[ skipping %dK ]"),
179 (int) (skipped / 1024));
182 logprintf (LOG_VERBOSE, "\n%5ldK", skipped / 1024);
183 for (; remainder >= dot_bytes; remainder -= dot_bytes)
185 if (dp->dots % opt.dot_spacing == 0)
186 logputs (LOG_VERBOSE, " ");
187 logputs (LOG_VERBOSE, ",");
190 assert (dp->dots < opt.dots_in_line);
192 dp->accumulated = remainder;
193 dp->rows = skipped / row_bytes;
200 print_percentage (long bytes, long expected)
202 int percentage = (int)(100.0 * bytes / expected);
203 logprintf (LOG_VERBOSE, "%3d%%", percentage);
207 print_elapsed (struct dot_progress *dp, long bytes)
209 long timer_value = wtimer_elapsed (dp->timer);
210 logprintf (LOG_VERBOSE, " @ %s",
211 rate (bytes, timer_value - dp->last_timer_value, 1));
212 dp->last_timer_value = timer_value;
215 /* Dot-progress backend for progress_update. */
218 dp_update (void *progress, long howmuch)
220 struct dot_progress *dp = progress;
221 int dot_bytes = opt.dot_bytes;
222 long row_bytes = opt.dot_bytes * opt.dots_in_line;
226 dp->accumulated += howmuch;
227 for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
230 logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
232 if (dp->dots % opt.dot_spacing == 0)
233 logputs (LOG_VERBOSE, " ");
234 logputs (LOG_VERBOSE, ".");
237 if (dp->dots >= opt.dots_in_line)
242 if (dp->total_length)
243 print_percentage (dp->rows * row_bytes, dp->total_length);
245 print_elapsed (dp, row_bytes - (dp->initial_length % row_bytes));
252 /* Dot-progress backend for progress_finish. */
255 dp_finish (void *progress)
257 struct dot_progress *dp = progress;
258 int dot_bytes = opt.dot_bytes;
259 long row_bytes = opt.dot_bytes * opt.dots_in_line;
264 for (i = dp->dots; i < opt.dots_in_line; i++)
266 if (i % opt.dot_spacing == 0)
267 logputs (LOG_VERBOSE, " ");
268 logputs (LOG_VERBOSE, " ");
270 if (dp->total_length)
272 print_percentage (dp->rows * row_bytes
273 + dp->dots * dot_bytes
278 print_elapsed (dp, dp->dots * dot_bytes
280 - dp->initial_length % row_bytes);
281 logputs (LOG_VERBOSE, "\n\n");
285 wtimer_delete (dp->timer);
289 /* This function interprets the progress "parameters". For example,
290 if Wget is invoked with --progress=bar:mega, it will set the
291 "dot-style" to "mega". Valid styles are default, binary, mega, and
295 dp_set_params (const char *params)
300 /* We use this to set the retrieval style. */
301 if (!strcasecmp (params, "default"))
303 /* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
305 opt.dot_bytes = 1024;
306 opt.dot_spacing = 10;
307 opt.dots_in_line = 50;
309 else if (!strcasecmp (params, "binary"))
311 /* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
313 opt.dot_bytes = 8192;
314 opt.dot_spacing = 16;
315 opt.dots_in_line = 48;
317 else if (!strcasecmp (params, "mega"))
319 /* "Mega" retrieval, for retrieving very long files; each dot is
320 64K, 8 dots in a cluster, 6 clusters (3M) in a line. */
321 opt.dot_bytes = 65536L;
323 opt.dots_in_line = 48;
325 else if (!strcasecmp (params, "giga"))
327 /* "Giga" retrieval, for retrieving very very *very* long files;
328 each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
330 opt.dot_bytes = (1L << 20);
332 opt.dots_in_line = 32;
336 _("Invalid dot style specification `%s'; leaving unchanged.\n"),
340 /* "Thermometer" (bar) progress. */
342 /* Assumed screen width if we can't find the real value. */
343 #define DEFAULT_SCREEN_WIDTH 80
345 /* Minimum screen width we'll try to work with. If this is too small,
346 create_image will overflow the buffer. */
347 #define MINIMUM_SCREEN_WIDTH 45
349 static int screen_width = DEFAULT_SCREEN_WIDTH;
351 struct bar_progress {
352 long initial_length; /* how many bytes have been downloaded
354 long total_length; /* expected total byte count when the
356 long count; /* bytes downloaded so far */
358 struct wget_timer *timer; /* timer used to measure the download
360 long last_update; /* time of the last screen update. */
362 int width; /* screen width at the time the
363 progress gauge was created. */
364 char *buffer; /* buffer where the bar "image" is
368 static void create_image PARAMS ((struct bar_progress *, long));
369 static void display_image PARAMS ((char *));
372 bar_create (long initial, long total)
374 struct bar_progress *bp = xmalloc (sizeof (struct bar_progress));
376 memset (bp, 0, sizeof (*bp));
378 bp->initial_length = initial;
379 bp->total_length = total;
380 bp->timer = wtimer_new ();
381 bp->width = screen_width;
382 bp->buffer = xmalloc (bp->width + 1);
384 logputs (LOG_VERBOSE, "\n");
386 create_image (bp, 0);
387 display_image (bp->buffer);
393 bar_update (void *progress, long howmuch)
395 struct bar_progress *bp = progress;
396 int force_update = 0;
397 long dltime = wtimer_elapsed (bp->timer);
399 bp->count += howmuch;
400 if (bp->count + bp->initial_length > bp->total_length)
401 /* We could be downloading more than total_length, e.g. when the
402 server sends an incorrect Content-Length header. In that case,
403 adjust bp->total_length to the new reality, so that the code in
404 create_image() that depends on total size being smaller or
405 equal to the expected size doesn't abort. */
406 bp->total_length = bp->count + bp->initial_length;
408 if (screen_width != bp->width)
410 bp->width = screen_width;
411 bp->buffer = xrealloc (bp->buffer, bp->width + 1);
414 if (dltime - bp->last_update < 200 && !force_update)
415 /* Don't update more often than every half a second. */
418 bp->last_update = dltime;
420 create_image (bp, dltime);
421 display_image (bp->buffer);
425 bar_finish (void *progress)
427 struct bar_progress *bp = progress;
429 create_image (bp, wtimer_elapsed (bp->timer));
430 display_image (bp->buffer);
432 logputs (LOG_VERBOSE, "\n\n");
435 wtimer_delete (bp->timer);
440 create_image (struct bar_progress *bp, long dltime)
442 char *p = bp->buffer;
443 long size = bp->initial_length + bp->count;
445 /* The progress bar should look like this:
446 xxx% |=======> | xx KB/s nnnnn ETA: 00:00
448 Calculate its geometry:
450 "xxx% " - percentage - 5 chars
451 "| ... | " - progress bar decorations - 3 chars
452 "1234.56 K/s " - dl rate - 12 chars
453 "nnnn " - downloaded bytes - 11 chars
454 "ETA: xx:xx:xx" - ETA - 13 chars
456 "=====>..." - progress bar content - the rest
458 int progress_len = screen_width - (5 + 3 + 12 + 11 + 13);
460 if (progress_len < 7)
464 if (bp->total_length > 0)
466 int percentage = (int)(100.0 * size / bp->total_length);
468 assert (percentage <= 100);
470 sprintf (p, "%3d%% ", percentage);
474 /* The progress bar: "|====> | " */
475 if (progress_len && bp->total_length > 0)
477 double fraction = (double)size / bp->total_length;
478 int dlsz = (int)(fraction * progress_len);
481 assert (dlsz <= progress_len);
488 /* Draw dlsz-1 '=' chars and one arrow char. */
494 while (p - begin < progress_len)
502 if (dltime && bp->count)
504 char *rt = rate (bp->count, dltime, 1);
511 strcpy (p, "----.-- KB/s ");
516 sprintf (p, _("%ld "), size);
519 /* "ETA: xx:xx:xx" */
520 if (bp->total_length > 0 && bp->count > 0)
522 int eta, eta_hrs, eta_min, eta_sec;
523 double tm_sofar = (double)dltime / 1000;
524 long bytes_remaining = bp->total_length - size;
526 eta = (int) (tm_sofar * bytes_remaining / bp->count);
528 eta_hrs = eta / 3600, eta %= 3600;
529 eta_min = eta / 60, eta %= 60;
532 /*printf ("\neta: %d, %d %d %d\n", eta, eta_hrs, eta_min, eta_sec);*/
533 /*printf ("\n%ld %f %ld %ld\n", dltime, tm_sofar, bytes_remaining, bp->count);*/
542 /* Bogus value, for whatever reason. We must avoid overflow. */
543 sprintf (p, "--:--");
544 else if (eta_hrs > 0)
545 sprintf (p, "%d:%02d:%02d", eta_hrs, eta_min, eta_sec);
547 sprintf (p, "%02d:%02d", eta_min, eta_sec);
550 else if (bp->total_length > 0)
552 strcpy (p, "ETA: --:--");
556 assert (p - bp->buffer <= screen_width);
558 while (p < bp->buffer + screen_width)
564 display_image (char *buf)
566 int len = strlen (buf);
567 char *del_buf = alloca (len + 1);
569 logputs (LOG_VERBOSE, buf);
571 memset (del_buf, '\b', len);
574 logputs (LOG_VERBOSE, del_buf);
578 bar_set_params (const char *ignored)
580 int sw = determine_screen_width ();
581 if (sw && sw >= MINIMUM_SCREEN_WIDTH)
586 progress_handle_sigwinch (int sig)
588 int sw = determine_screen_width ();
589 if (sw && sw >= MINIMUM_SCREEN_WIDTH)