]> sjero.net Git - wget/blob - src/progress.c
[svn] Fix K&R incompatibilities reported by `gcc -Wtraditional'.
[wget] / src / progress.c
1 /* Download progress.
2    Copyright (C) 2001, 2002 Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
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.
10
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.
15
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.
19
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.  */
29
30 #include <config.h>
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #ifdef HAVE_STRING_H
35 # include <string.h>
36 #else
37 # include <strings.h>
38 #endif /* HAVE_STRING_H */
39 #include <assert.h>
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
43 #ifdef HAVE_SIGNAL_H
44 # include <signal.h>
45 #endif
46
47 #include "wget.h"
48 #include "progress.h"
49 #include "utils.h"
50 #include "retr.h"
51
52 struct progress_implementation {
53   const char *name;
54   int interactive;
55   void *(*create) PARAMS ((wgint, wgint));
56   void (*update) PARAMS ((void *, wgint, double));
57   void (*finish) PARAMS ((void *, double));
58   void (*set_params) PARAMS ((const char *));
59 };
60
61 /* Necessary forward declarations. */
62
63 static void *dot_create PARAMS ((wgint, wgint));
64 static void dot_update PARAMS ((void *, wgint, double));
65 static void dot_finish PARAMS ((void *, double));
66 static void dot_set_params PARAMS ((const char *));
67
68 static void *bar_create PARAMS ((wgint, wgint));
69 static void bar_update PARAMS ((void *, wgint, double));
70 static void bar_finish PARAMS ((void *, double));
71 static void bar_set_params PARAMS ((const char *));
72
73 static struct progress_implementation implementations[] = {
74   { "dot", 0, dot_create, dot_update, dot_finish, dot_set_params },
75   { "bar", 1, bar_create, bar_update, bar_finish, bar_set_params }
76 };
77 static struct progress_implementation *current_impl;
78 static int current_impl_locked;
79
80 /* Progress implementation used by default.  Can be overriden in
81    wgetrc or by the fallback one.  */
82
83 #define DEFAULT_PROGRESS_IMPLEMENTATION "bar"
84
85 /* Fallnback progress implementation should be something that works
86    under all display types.  If you put something other than "dot"
87    here, remember that bar_set_params tries to switch to this if we're
88    not running on a TTY.  So changing this to "bar" could cause
89    infloop.  */
90
91 #define FALLBACK_PROGRESS_IMPLEMENTATION "dot"
92
93 /* Return non-zero if NAME names a valid progress bar implementation.
94    The characters after the first : will be ignored.  */
95
96 int
97 valid_progress_implementation_p (const char *name)
98 {
99   int i;
100   struct progress_implementation *pi = implementations;
101   char *colon = strchr (name, ':');
102   int namelen = colon ? colon - name : strlen (name);
103
104   for (i = 0; i < countof (implementations); i++, pi++)
105     if (!strncmp (pi->name, name, namelen))
106       return 1;
107   return 0;
108 }
109
110 /* Set the progress implementation to NAME.  */
111
112 void
113 set_progress_implementation (const char *name)
114 {
115   int i, namelen;
116   struct progress_implementation *pi = implementations;
117   char *colon;
118
119   if (!name)
120     name = DEFAULT_PROGRESS_IMPLEMENTATION;
121
122   colon = strchr (name, ':');
123   namelen = colon ? colon - name : strlen (name);
124
125   for (i = 0; i < countof (implementations); i++, pi++)
126     if (!strncmp (pi->name, name, namelen))
127       {
128         current_impl = pi;
129         current_impl_locked = 0;
130
131         if (colon)
132           /* We call pi->set_params even if colon is NULL because we
133              want to give the implementation a chance to set up some
134              things it needs to run.  */
135           ++colon;
136
137         if (pi->set_params)
138           pi->set_params (colon);
139         return;
140       }
141   abort ();
142 }
143
144 static int output_redirected;
145
146 void
147 progress_schedule_redirect (void)
148 {
149   output_redirected = 1;
150 }
151
152 /* Create a progress gauge.  INITIAL is the number of bytes the
153    download starts from (zero if the download starts from scratch).
154    TOTAL is the expected total number of bytes in this download.  If
155    TOTAL is zero, it means that the download size is not known in
156    advance.  */
157
158 void *
159 progress_create (wgint initial, wgint total)
160 {
161   /* Check if the log status has changed under our feet. */
162   if (output_redirected)
163     {
164       if (!current_impl_locked)
165         set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
166       output_redirected = 0;
167     }
168
169   return current_impl->create (initial, total);
170 }
171
172 /* Return non-zero if the progress gauge is "interactive", i.e. if it
173    can profit from being called regularly even in absence of data.
174    The progress bar is interactive because it regularly updates the
175    ETA and current update.  */
176
177 int
178 progress_interactive_p (void *progress)
179 {
180   return current_impl->interactive;
181 }
182
183 /* Inform the progress gauge of newly received bytes.  DLTIME is the
184    time in milliseconds since the beginning of the download.  */
185
186 void
187 progress_update (void *progress, wgint howmuch, double dltime)
188 {
189   current_impl->update (progress, howmuch, dltime);
190 }
191
192 /* Tell the progress gauge to clean up.  Calling this will free the
193    PROGRESS object, the further use of which is not allowed.  */
194
195 void
196 progress_finish (void *progress, double dltime)
197 {
198   current_impl->finish (progress, dltime);
199 }
200 \f
201 /* Dot-printing. */
202
203 struct dot_progress {
204   wgint initial_length;         /* how many bytes have been downloaded
205                                    previously. */
206   wgint total_length;           /* expected total byte count when the
207                                    download finishes */
208
209   int accumulated;
210
211   int rows;                     /* number of rows printed so far */
212   int dots;                     /* number of dots printed in this row */
213   double last_timer_value;
214 };
215
216 /* Dot-progress backend for progress_create. */
217
218 static void *
219 dot_create (wgint initial, wgint total)
220 {
221   struct dot_progress *dp = xnew0 (struct dot_progress);
222   dp->initial_length = initial;
223   dp->total_length   = total;
224
225   if (dp->initial_length)
226     {
227       int dot_bytes = opt.dot_bytes;
228       wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
229
230       int remainder = (int) (dp->initial_length % row_bytes);
231       wgint skipped = dp->initial_length - remainder;
232
233       if (skipped)
234         {
235           int skipped_k = (int) (skipped / 1024); /* skipped amount in K */
236           int skipped_k_len = numdigit (skipped_k);
237           if (skipped_k_len < 5)
238             skipped_k_len = 5;
239
240           /* Align the [ skipping ... ] line with the dots.  To do
241              that, insert the number of spaces equal to the number of
242              digits in the skipped amount in K.  */
243           logprintf (LOG_VERBOSE, _("\n%*s[ skipping %dK ]"),
244                      2 + skipped_k_len, "", skipped_k);
245         }
246
247       logprintf (LOG_VERBOSE, "\n%5ldK", (long) (skipped / 1024));
248       for (; remainder >= dot_bytes; remainder -= dot_bytes)
249         {
250           if (dp->dots % opt.dot_spacing == 0)
251             logputs (LOG_VERBOSE, " ");
252           logputs (LOG_VERBOSE, ",");
253           ++dp->dots;
254         }
255       assert (dp->dots < opt.dots_in_line);
256
257       dp->accumulated = remainder;
258       dp->rows = skipped / row_bytes;
259     }
260
261   return dp;
262 }
263
264 static void
265 print_percentage (wgint bytes, wgint expected)
266 {
267   int percentage = (int)(100.0 * bytes / expected);
268   logprintf (LOG_VERBOSE, "%3d%%", percentage);
269 }
270
271 static void
272 print_download_speed (struct dot_progress *dp, wgint bytes, double dltime)
273 {
274   logprintf (LOG_VERBOSE, " %s",
275              retr_rate (bytes, dltime - dp->last_timer_value, 1));
276   dp->last_timer_value = dltime;
277 }
278
279 /* Dot-progress backend for progress_update. */
280
281 static void
282 dot_update (void *progress, wgint howmuch, double dltime)
283 {
284   struct dot_progress *dp = progress;
285   int dot_bytes = opt.dot_bytes;
286   wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
287
288   log_set_flush (0);
289
290   dp->accumulated += howmuch;
291   for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
292     {
293       if (dp->dots == 0)
294         logprintf (LOG_VERBOSE, "\n%5ldK", (long) (dp->rows * row_bytes / 1024));
295
296       if (dp->dots % opt.dot_spacing == 0)
297         logputs (LOG_VERBOSE, " ");
298       logputs (LOG_VERBOSE, ".");
299
300       ++dp->dots;
301       if (dp->dots >= opt.dots_in_line)
302         {
303           wgint row_qty = row_bytes;
304           if (dp->rows == dp->initial_length / row_bytes)
305             row_qty -= dp->initial_length % row_bytes;
306
307           ++dp->rows;
308           dp->dots = 0;
309
310           if (dp->total_length)
311             print_percentage (dp->rows * row_bytes, dp->total_length);
312           print_download_speed (dp, row_qty, dltime);
313         }
314     }
315
316   log_set_flush (1);
317 }
318
319 /* Dot-progress backend for progress_finish. */
320
321 static void
322 dot_finish (void *progress, double dltime)
323 {
324   struct dot_progress *dp = progress;
325   int dot_bytes = opt.dot_bytes;
326   wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
327   int i;
328
329   log_set_flush (0);
330
331   if (dp->dots == 0)
332     logprintf (LOG_VERBOSE, "\n%5ldK", (long) (dp->rows * row_bytes / 1024));
333   for (i = dp->dots; i < opt.dots_in_line; i++)
334     {
335       if (i % opt.dot_spacing == 0)
336         logputs (LOG_VERBOSE, " ");
337       logputs (LOG_VERBOSE, " ");
338     }
339   if (dp->total_length)
340     {
341       print_percentage (dp->rows * row_bytes
342                         + dp->dots * dot_bytes
343                         + dp->accumulated,
344                         dp->total_length);
345     }
346
347   {
348     wgint row_qty = dp->dots * dot_bytes + dp->accumulated;
349     if (dp->rows == dp->initial_length / row_bytes)
350       row_qty -= dp->initial_length % row_bytes;
351     print_download_speed (dp, row_qty, dltime);
352   }
353
354   logputs (LOG_VERBOSE, "\n\n");
355   log_set_flush (0);
356
357   xfree (dp);
358 }
359
360 /* This function interprets the progress "parameters".  For example,
361    if Wget is invoked with --progress=dot:mega, it will set the
362    "dot-style" to "mega".  Valid styles are default, binary, mega, and
363    giga.  */
364
365 static void
366 dot_set_params (const char *params)
367 {
368   if (!params || !*params)
369     params = opt.dot_style;
370
371   if (!params)
372     return;
373
374   /* We use this to set the retrieval style.  */
375   if (!strcasecmp (params, "default"))
376     {
377       /* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
378          line.  */
379       opt.dot_bytes = 1024;
380       opt.dot_spacing = 10;
381       opt.dots_in_line = 50;
382     }
383   else if (!strcasecmp (params, "binary"))
384     {
385       /* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
386          (384K) in a line.  */
387       opt.dot_bytes = 8192;
388       opt.dot_spacing = 16;
389       opt.dots_in_line = 48;
390     }
391   else if (!strcasecmp (params, "mega"))
392     {
393       /* "Mega" retrieval, for retrieving very long files; each dot is
394          64K, 8 dots in a cluster, 6 clusters (3M) in a line.  */
395       opt.dot_bytes = 65536L;
396       opt.dot_spacing = 8;
397       opt.dots_in_line = 48;
398     }
399   else if (!strcasecmp (params, "giga"))
400     {
401       /* "Giga" retrieval, for retrieving very very *very* long files;
402          each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
403          line.  */
404       opt.dot_bytes = (1L << 20);
405       opt.dot_spacing = 8;
406       opt.dots_in_line = 32;
407     }
408   else
409     fprintf (stderr,
410              _("Invalid dot style specification `%s'; leaving unchanged.\n"),
411              params);
412 }
413 \f
414 /* "Thermometer" (bar) progress. */
415
416 /* Assumed screen width if we can't find the real value.  */
417 #define DEFAULT_SCREEN_WIDTH 80
418
419 /* Minimum screen width we'll try to work with.  If this is too small,
420    create_image will overflow the buffer.  */
421 #define MINIMUM_SCREEN_WIDTH 45
422
423 /* The last known screen width.  This can be updated by the code that
424    detects that SIGWINCH was received (but it's never updated from the
425    signal handler).  */
426 static int screen_width;
427
428 /* A flag that, when set, means SIGWINCH was received.  */
429 static volatile sig_atomic_t received_sigwinch;
430
431 /* Size of the download speed history ring. */
432 #define DLSPEED_HISTORY_SIZE 20
433
434 /* The minimum time length of a history sample.  By default, each
435    sample is at least 150ms long, which means that, over the course of
436    20 samples, "current" download speed spans at least 3s into the
437    past.  */
438 #define DLSPEED_SAMPLE_MIN 150
439
440 /* The time after which the download starts to be considered
441    "stalled", i.e. the current bandwidth is not printed and the recent
442    download speeds are scratched.  */
443 #define STALL_START_TIME 5000
444
445 struct bar_progress {
446   wgint initial_length;         /* how many bytes have been downloaded
447                                    previously. */
448   wgint total_length;           /* expected total byte count when the
449                                    download finishes */
450   wgint count;                  /* bytes downloaded so far */
451
452   double last_screen_update;    /* time of the last screen update,
453                                    measured since the beginning of
454                                    download. */
455
456   int width;                    /* screen width we're using at the
457                                    time the progress gauge was
458                                    created.  this is different from
459                                    the screen_width global variable in
460                                    that the latter can be changed by a
461                                    signal. */
462   char *buffer;                 /* buffer where the bar "image" is
463                                    stored. */
464   int tick;                     /* counter used for drawing the
465                                    progress bar where the total size
466                                    is not known. */
467
468   /* The following variables (kept in a struct for namespace reasons)
469      keep track of recent download speeds.  See bar_update() for
470      details.  */
471   struct bar_progress_hist {
472     int pos;
473     wgint times[DLSPEED_HISTORY_SIZE];
474     wgint bytes[DLSPEED_HISTORY_SIZE];
475
476     /* The sum of times and bytes respectively, maintained for
477        efficiency. */
478     wgint total_time;
479     wgint total_bytes;
480   } hist;
481
482   double recent_start;          /* timestamp of beginning of current
483                                    position. */
484   wgint recent_bytes;           /* bytes downloaded so far. */
485
486   int stalled;                  /* set when no data arrives for longer
487                                    than STALL_START_TIME, then reset
488                                    when new data arrives. */
489
490   /* create_image() uses these to make sure that ETA information
491      doesn't flicker. */
492   double last_eta_time;         /* time of the last update to download
493                                    speed and ETA, measured since the
494                                    beginning of download. */
495   wgint last_eta_value;
496 };
497
498 static void create_image PARAMS ((struct bar_progress *, double));
499 static void display_image PARAMS ((char *));
500
501 static void *
502 bar_create (wgint initial, wgint total)
503 {
504   struct bar_progress *bp = xnew0 (struct bar_progress);
505
506   /* In theory, our callers should take care of this pathological
507      case, but it can sometimes happen. */
508   if (initial > total)
509     total = initial;
510
511   bp->initial_length = initial;
512   bp->total_length   = total;
513
514   /* Initialize screen_width if this hasn't been done or if it might
515      have changed, as indicated by receiving SIGWINCH.  */
516   if (!screen_width || received_sigwinch)
517     {
518       screen_width = determine_screen_width ();
519       if (!screen_width)
520         screen_width = DEFAULT_SCREEN_WIDTH;
521       else if (screen_width < MINIMUM_SCREEN_WIDTH)
522         screen_width = MINIMUM_SCREEN_WIDTH;
523       received_sigwinch = 0;
524     }
525
526   /* - 1 because we don't want to use the last screen column. */
527   bp->width = screen_width - 1;
528   /* + 1 for the terminating zero. */
529   bp->buffer = xmalloc (bp->width + 1);
530
531   logputs (LOG_VERBOSE, "\n");
532
533   create_image (bp, 0.0);
534   display_image (bp->buffer);
535
536   return bp;
537 }
538
539 static void update_speed_ring PARAMS ((struct bar_progress *, wgint, double));
540
541 static void
542 bar_update (void *progress, wgint howmuch, double dltime)
543 {
544   struct bar_progress *bp = progress;
545   int force_screen_update = 0;
546
547   bp->count += howmuch;
548   if (bp->total_length > 0
549       && bp->count + bp->initial_length > bp->total_length)
550     /* We could be downloading more than total_length, e.g. when the
551        server sends an incorrect Content-Length header.  In that case,
552        adjust bp->total_length to the new reality, so that the code in
553        create_image() that depends on total size being smaller or
554        equal to the expected size doesn't abort.  */
555     bp->total_length = bp->initial_length + bp->count;
556
557   update_speed_ring (bp, howmuch, dltime);
558
559   /* If SIGWINCH (the window size change signal) been received,
560      determine the new screen size and update the screen.  */
561   if (received_sigwinch)
562     {
563       int old_width = screen_width;
564       screen_width = determine_screen_width ();
565       if (!screen_width)
566         screen_width = DEFAULT_SCREEN_WIDTH;
567       else if (screen_width < MINIMUM_SCREEN_WIDTH)
568         screen_width = MINIMUM_SCREEN_WIDTH;
569       if (screen_width != old_width)
570         {
571           bp->width = screen_width - 1;
572           bp->buffer = xrealloc (bp->buffer, bp->width + 1);
573           force_screen_update = 1;
574         }
575       received_sigwinch = 0;
576     }
577
578   if (dltime - bp->last_screen_update < 200 && !force_screen_update)
579     /* Don't update more often than five times per second. */
580     return;
581
582   create_image (bp, dltime);
583   display_image (bp->buffer);
584   bp->last_screen_update = dltime;
585 }
586
587 static void
588 bar_finish (void *progress, double dltime)
589 {
590   struct bar_progress *bp = progress;
591
592   if (bp->total_length > 0
593       && bp->count + bp->initial_length > bp->total_length)
594     /* See bar_update() for explanation. */
595     bp->total_length = bp->initial_length + bp->count;
596
597   create_image (bp, dltime);
598   display_image (bp->buffer);
599
600   logputs (LOG_VERBOSE, "\n\n");
601
602   xfree (bp->buffer);
603   xfree (bp);
604 }
605
606 /* This code attempts to maintain the notion of a "current" download
607    speed, over the course of no less than 3s.  (Shorter intervals
608    produce very erratic results.)
609
610    To do so, it samples the speed in 150ms intervals and stores the
611    recorded samples in a FIFO history ring.  The ring stores no more
612    than 20 intervals, hence the history covers the period of at least
613    three seconds and at most 20 reads into the past.  This method
614    should produce reasonable results for downloads ranging from very
615    slow to very fast.
616
617    The idea is that for fast downloads, we get the speed over exactly
618    the last three seconds.  For slow downloads (where a network read
619    takes more than 150ms to complete), we get the speed over a larger
620    time period, as large as it takes to complete thirty reads.  This
621    is good because slow downloads tend to fluctuate more and a
622    3-second average would be too erratic.  */
623
624 static void
625 update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime)
626 {
627   struct bar_progress_hist *hist = &bp->hist;
628   double recent_age = dltime - bp->recent_start;
629
630   /* Update the download count. */
631   bp->recent_bytes += howmuch;
632
633   /* For very small time intervals, we return after having updated the
634      "recent" download count.  When its age reaches or exceeds minimum
635      sample time, it will be recorded in the history ring.  */
636   if (recent_age < DLSPEED_SAMPLE_MIN)
637     return;
638
639   if (howmuch == 0)
640     {
641       /* If we're not downloading anything, we might be stalling,
642          i.e. not downloading anything for an extended period of time.
643          Since 0-reads do not enter the history ring, recent_age
644          effectively measures the time since last read.  */
645       if (recent_age >= STALL_START_TIME)
646         {
647           /* If we're stalling, reset the ring contents because it's
648              stale and because it will make bar_update stop printing
649              the (bogus) current bandwidth.  */
650           bp->stalled = 1;
651           xzero (*hist);
652           bp->recent_bytes = 0;
653         }
654       return;
655     }
656
657   /* We now have a non-zero amount of to store to the speed ring.  */
658
659   /* If the stall status was acquired, reset it. */
660   if (bp->stalled)
661     {
662       bp->stalled = 0;
663       /* "recent_age" includes the the entired stalled period, which
664          could be very long.  Don't update the speed ring with that
665          value because the current bandwidth would start too small.
666          Start with an arbitrary (but more reasonable) time value and
667          let it level out.  */
668       recent_age = 1000;
669     }
670
671   /* Store "recent" bytes and download time to history ring at the
672      position POS.  */
673
674   /* To correctly maintain the totals, first invalidate existing data
675      (least recent in time) at this position. */
676   hist->total_time  -= hist->times[hist->pos];
677   hist->total_bytes -= hist->bytes[hist->pos];
678
679   /* Now store the new data and update the totals. */
680   hist->times[hist->pos] = recent_age;
681   hist->bytes[hist->pos] = bp->recent_bytes;
682   hist->total_time  += recent_age;
683   hist->total_bytes += bp->recent_bytes;
684
685   /* Start a new "recent" period. */
686   bp->recent_start = dltime;
687   bp->recent_bytes = 0;
688
689   /* Advance the current ring position. */
690   if (++hist->pos == DLSPEED_HISTORY_SIZE)
691     hist->pos = 0;
692
693 #if 0
694   /* Sledgehammer check to verify that the totals are accurate. */
695   {
696     int i;
697     double sumt = 0, sumb = 0;
698     for (i = 0; i < DLSPEED_HISTORY_SIZE; i++)
699       {
700         sumt += hist->times[i];
701         sumb += hist->bytes[i];
702       }
703     assert (sumt == hist->total_time);
704     assert (sumb == hist->total_bytes);
705   }
706 #endif
707 }
708
709 #define APPEND_LITERAL(s) do {                  \
710   memcpy (p, s, sizeof (s) - 1);                \
711   p += sizeof (s) - 1;                          \
712 } while (0)
713
714 #ifndef MAX
715 # define MAX(a, b) ((a) >= (b) ? (a) : (b))
716 #endif
717
718 static void
719 create_image (struct bar_progress *bp, double dl_total_time)
720 {
721   char *p = bp->buffer;
722   wgint size = bp->initial_length + bp->count;
723
724   char *size_legible = with_thousand_seps (size);
725   int size_legible_len = strlen (size_legible);
726
727   struct bar_progress_hist *hist = &bp->hist;
728
729   /* The progress bar should look like this:
730      xx% [=======>             ] nn,nnn 12.34K/s ETA 00:00
731
732      Calculate the geometry.  The idea is to assign as much room as
733      possible to the progress bar.  The other idea is to never let
734      things "jitter", i.e. pad elements that vary in size so that
735      their variance does not affect the placement of other elements.
736      It would be especially bad for the progress bar to be resized
737      randomly.
738
739      "xx% " or "100%"  - percentage               - 4 chars
740      "[]"              - progress bar decorations - 2 chars
741      " nnn,nnn,nnn"    - downloaded bytes         - 12 chars or very rarely more
742      " 1012.56K/s"     - dl rate                  - 11 chars
743      " ETA xx:xx:xx"   - ETA                      - 13 chars
744
745      "=====>..."       - progress bar             - the rest
746   */
747   int dlbytes_size = 1 + MAX (size_legible_len, 11);
748   int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);
749
750   if (progress_size < 5)
751     progress_size = 0;
752
753   /* "xx% " */
754   if (bp->total_length > 0)
755     {
756       int percentage = (int)(100.0 * size / bp->total_length);
757
758       assert (percentage <= 100);
759
760       if (percentage < 100)
761         sprintf (p, "%2d%% ", percentage);
762       else
763         strcpy (p, "100%");
764       p += 4;
765     }
766   else
767     APPEND_LITERAL ("    ");
768
769   /* The progress bar: "[====>      ]" or "[++==>      ]". */
770   if (progress_size && bp->total_length > 0)
771     {
772       /* Size of the initial portion. */
773       int insz = (double)bp->initial_length / bp->total_length * progress_size;
774
775       /* Size of the downloaded portion. */
776       int dlsz = (double)size / bp->total_length * progress_size;
777
778       char *begin;
779       int i;
780
781       assert (dlsz <= progress_size);
782       assert (insz <= dlsz);
783
784       *p++ = '[';
785       begin = p;
786
787       /* Print the initial portion of the download with '+' chars, the
788          rest with '=' and one '>'.  */
789       for (i = 0; i < insz; i++)
790         *p++ = '+';
791       dlsz -= insz;
792       if (dlsz > 0)
793         {
794           for (i = 0; i < dlsz - 1; i++)
795             *p++ = '=';
796           *p++ = '>';
797         }
798
799       while (p - begin < progress_size)
800         *p++ = ' ';
801       *p++ = ']';
802     }
803   else if (progress_size)
804     {
805       /* If we can't draw a real progress bar, then at least show
806          *something* to the user.  */
807       int ind = bp->tick % (progress_size * 2 - 6);
808       int i, pos;
809
810       /* Make the star move in two directions. */
811       if (ind < progress_size - 2)
812         pos = ind + 1;
813       else
814         pos = progress_size - (ind - progress_size + 5);
815
816       *p++ = '[';
817       for (i = 0; i < progress_size; i++)
818         {
819           if      (i == pos - 1) *p++ = '<';
820           else if (i == pos    ) *p++ = '=';
821           else if (i == pos + 1) *p++ = '>';
822           else
823             *p++ = ' ';
824         }
825       *p++ = ']';
826
827       ++bp->tick;
828     }
829
830   /* " 234,567,890" */
831   sprintf (p, " %-11s", with_thousand_seps (size));
832   p += strlen (p);
833
834   /* " 1012.45K/s" */
835   if (hist->total_time && hist->total_bytes)
836     {
837       static const char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
838       int units = 0;
839       /* Calculate the download speed using the history ring and
840          recent data that hasn't made it to the ring yet.  */
841       wgint dlquant = hist->total_bytes + bp->recent_bytes;
842       double dltime = hist->total_time + (dl_total_time - bp->recent_start);
843       double dlspeed = calc_rate (dlquant, dltime, &units);
844       sprintf (p, " %7.2f%s", dlspeed, short_units[units]);
845       p += strlen (p);
846     }
847   else
848     APPEND_LITERAL ("   --.--K/s");
849
850   /* " ETA xx:xx:xx"; wait for three seconds before displaying the ETA.
851      That's because the ETA value needs a while to become
852      reliable.  */
853   if (bp->total_length > 0 && bp->count > 0 && dl_total_time > 3000)
854     {
855       wgint eta;
856       int eta_hrs, eta_min, eta_sec;
857
858       /* Don't change the value of ETA more than approximately once
859          per second; doing so would cause flashing without providing
860          any value to the user. */
861       if (bp->total_length != size
862           && bp->last_eta_value != 0
863           && dl_total_time - bp->last_eta_time < 900)
864         eta = bp->last_eta_value;
865       else
866         {
867           /* Calculate ETA using the average download speed to predict
868              the future speed.  If you want to use a speed averaged
869              over a more recent period, replace dl_total_time with
870              hist->total_time and bp->count with hist->total_bytes.
871              I found that doing that results in a very jerky and
872              ultimately unreliable ETA.  */
873           double time_sofar = (double)dl_total_time / 1000;
874           wgint bytes_remaining = bp->total_length - size;
875           eta = (wgint) (time_sofar * bytes_remaining / bp->count);
876           bp->last_eta_value = eta;
877           bp->last_eta_time = dl_total_time;
878         }
879
880       eta_hrs = eta / 3600, eta %= 3600;
881       eta_min = eta / 60,   eta %= 60;
882       eta_sec = eta;
883
884       if (eta_hrs > 99)
885         goto no_eta;
886
887       if (eta_hrs == 0)
888         {
889           /* Hours not printed: pad with three spaces. */
890           APPEND_LITERAL ("   ");
891           sprintf (p, " ETA %02d:%02d", eta_min, eta_sec);
892         }
893       else
894         {
895           if (eta_hrs < 10)
896             /* Hours printed with one digit: pad with one space. */
897             *p++ = ' ';
898           sprintf (p, " ETA %d:%02d:%02d", eta_hrs, eta_min, eta_sec);
899         }
900       p += strlen (p);
901     }
902   else if (bp->total_length > 0)
903     {
904     no_eta:
905       APPEND_LITERAL ("             ");
906     }
907
908   assert (p - bp->buffer <= bp->width);
909
910   while (p < bp->buffer + bp->width)
911     *p++ = ' ';
912   *p = '\0';
913 }
914
915 /* Print the contents of the buffer as a one-line ASCII "image" so
916    that it can be overwritten next time.  */
917
918 static void
919 display_image (char *buf)
920 {
921   int old = log_set_save_context (0);
922   logputs (LOG_VERBOSE, "\r");
923   logputs (LOG_VERBOSE, buf);
924   log_set_save_context (old);
925 }
926
927 static void
928 bar_set_params (const char *params)
929 {
930   char *term = getenv ("TERM");
931
932   if (params
933       && 0 == strcmp (params, "force"))
934     current_impl_locked = 1;
935
936   if ((opt.lfilename
937 #ifdef HAVE_ISATTY
938        /* The progress bar doesn't make sense if the output is not a
939           TTY -- when logging to file, it is better to review the
940           dots.  */
941        || !isatty (fileno (stderr))
942 #endif
943        /* Normally we don't depend on terminal type because the
944           progress bar only uses ^M to move the cursor to the
945           beginning of line, which works even on dumb terminals.  But
946           Jamie Zawinski reports that ^M and ^H tricks don't work in
947           Emacs shell buffers, and only make a mess.  */
948        || (term && 0 == strcmp (term, "emacs"))
949        )
950       && !current_impl_locked)
951     {
952       /* We're not printing to a TTY, so revert to the fallback
953          display.  #### We're recursively calling
954          set_progress_implementation here, which is slightly kludgy.
955          It would be nicer if we provided that function a return value
956          indicating a failure of some sort.  */
957       set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
958       return;
959     }
960 }
961
962 #ifdef SIGWINCH
963 RETSIGTYPE
964 progress_handle_sigwinch (int sig)
965 {
966   received_sigwinch = 1;
967   signal (SIGWINCH, progress_handle_sigwinch);
968 }
969 #endif