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