]> sjero.net Git - wget/blob - src/progress.c
[svn] Print the current download speed, rather than the average.
[wget] / src / progress.c
1 /* Download progress.
2    Copyright (C) 2001 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 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #ifdef HAVE_STRING_H
25 # include <string.h>
26 #else
27 # include <strings.h>
28 #endif /* HAVE_STRING_H */
29 #include <assert.h>
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
33 #ifdef HAVE_SIGNAL_H
34 # include <signal.h>
35 #endif
36
37 #include "wget.h"
38 #include "progress.h"
39 #include "utils.h"
40 #include "retr.h"
41
42 struct progress_implementation {
43   char *name;
44   void *(*create) (long, long);
45   void (*update) (void *, long, long);
46   void (*finish) (void *, long);
47   void (*set_params) (const char *);
48 };
49
50 /* Necessary forward declarations. */
51
52 static void *dot_create PARAMS ((long, long));
53 static void dot_update PARAMS ((void *, long, long));
54 static void dot_finish PARAMS ((void *, long));
55 static void dot_set_params PARAMS ((const char *));
56
57 static void *bar_create PARAMS ((long, long));
58 static void bar_update PARAMS ((void *, long, long));
59 static void bar_finish PARAMS ((void *, long));
60 static void bar_set_params PARAMS ((const char *));
61
62 static struct progress_implementation implementations[] = {
63   { "dot", dot_create, dot_update, dot_finish, dot_set_params },
64   { "bar", bar_create, bar_update, bar_finish, bar_set_params }
65 };
66 static struct progress_implementation *current_impl;
67 static int current_impl_locked;
68
69 /* Progress implementation used by default.  Can be overriden in
70    wgetrc or by the fallback one.  */
71
72 #define DEFAULT_PROGRESS_IMPLEMENTATION "bar"
73
74 /* Fallnback progress implementation should be something that works
75    under all display types.  If you put something other than "dot"
76    here, remember that bar_set_params tries to switch to this if we're
77    not running on a TTY.  So changing this to "bar" could cause
78    infloop.  */
79
80 #define FALLBACK_PROGRESS_IMPLEMENTATION "dot"
81
82 /* Return non-zero if NAME names a valid progress bar implementation.
83    The characters after the first : will be ignored.  */
84
85 int
86 valid_progress_implementation_p (const char *name)
87 {
88   int i = 0;
89   struct progress_implementation *pi = implementations;
90   char *colon = strchr (name, ':');
91   int namelen = colon ? colon - name : strlen (name);
92
93   for (i = 0; i < ARRAY_SIZE (implementations); i++, pi++)
94     if (!strncmp (pi->name, name, namelen))
95       return 1;
96   return 0;
97 }
98
99 /* Set the progress implementation to NAME.  */
100
101 void
102 set_progress_implementation (const char *name)
103 {
104   int i, namelen;
105   struct progress_implementation *pi = implementations;
106   char *colon;
107
108   if (!name)
109     name = DEFAULT_PROGRESS_IMPLEMENTATION;
110
111   colon = strchr (name, ':');
112   namelen = colon ? colon - name : strlen (name);
113
114   for (i = 0; i < ARRAY_SIZE (implementations); i++, pi++)
115     if (!strncmp (pi->name, name, namelen))
116       {
117         current_impl = pi;
118         current_impl_locked = 0;
119
120         if (colon)
121           /* We call pi->set_params even if colon is NULL because we
122              want to give the implementation a chance to set up some
123              things it needs to run.  */
124           ++colon;
125
126         if (pi->set_params)
127           pi->set_params (colon);
128         return;
129       }
130   abort ();
131 }
132
133 static int output_redirected;
134
135 void
136 progress_schedule_redirect (void)
137 {
138   output_redirected = 1;
139 }
140
141 /* Create a progress gauge.  INITIAL is the number of bytes the
142    download starts from (zero if the download starts from scratch).
143    TOTAL is the expected total number of bytes in this download.  If
144    TOTAL is zero, it means that the download size is not known in
145    advance.  */
146
147 void *
148 progress_create (long initial, long total)
149 {
150   /* Check if the log status has changed under our feet. */
151   if (output_redirected)
152     {
153       if (!current_impl_locked)
154         set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
155       output_redirected = 0;
156     }
157
158   return current_impl->create (initial, total);
159 }
160
161 /* Inform the progress gauge of newly received bytes.  DLTIME is the
162    time in milliseconds since the beginning of the download.  */
163
164 void
165 progress_update (void *progress, long howmuch, long dltime)
166 {
167   current_impl->update (progress, howmuch, dltime);
168 }
169
170 /* Tell the progress gauge to clean up.  Calling this will free the
171    PROGRESS object, the further use of which is not allowed.  */
172
173 void
174 progress_finish (void *progress, long dltime)
175 {
176   current_impl->finish (progress, dltime);
177 }
178 \f
179 /* Dot-printing. */
180
181 struct dot_progress {
182   long initial_length;          /* how many bytes have been downloaded
183                                    previously. */
184   long total_length;            /* expected total byte count when the
185                                    download finishes */
186
187   int accumulated;
188
189   int rows;                     /* number of rows printed so far */
190   int dots;                     /* number of dots printed in this row */
191   long last_timer_value;
192 };
193
194 /* Dot-progress backend for progress_create. */
195
196 static void *
197 dot_create (long initial, long total)
198 {
199   struct dot_progress *dp = xmalloc (sizeof (struct dot_progress));
200
201   memset (dp, 0, sizeof (*dp));
202
203   dp->initial_length = initial;
204   dp->total_length   = total;
205
206   if (dp->initial_length)
207     {
208       int dot_bytes = opt.dot_bytes;
209       long row_bytes = opt.dot_bytes * opt.dots_in_line;
210
211       int remainder = (int) (dp->initial_length % row_bytes);
212       long skipped = dp->initial_length - remainder;
213
214       if (skipped)
215         {
216           int skipped_k = (int) (skipped / 1024); /* skipped amount in K */
217           int skipped_k_len = numdigit (skipped_k);
218           if (skipped_k_len < 5)
219             skipped_k_len = 5;
220
221           /* Align the [ skipping ... ] line with the dots.  To do
222              that, insert the number of spaces equal to the number of
223              digits in the skipped amount in K.  */
224           logprintf (LOG_VERBOSE, _("\n%*s[ skipping %dK ]"),
225                      2 + skipped_k_len, "", skipped_k);
226         }
227
228       logprintf (LOG_VERBOSE, "\n%5ldK", skipped / 1024);
229       for (; remainder >= dot_bytes; remainder -= dot_bytes)
230         {
231           if (dp->dots % opt.dot_spacing == 0)
232             logputs (LOG_VERBOSE, " ");
233           logputs (LOG_VERBOSE, ",");
234           ++dp->dots;
235         }
236       assert (dp->dots < opt.dots_in_line);
237
238       dp->accumulated = remainder;
239       dp->rows = skipped / row_bytes;
240     }
241
242   return dp;
243 }
244
245 static void
246 print_percentage (long bytes, long expected)
247 {
248   int percentage = (int)(100.0 * bytes / expected);
249   logprintf (LOG_VERBOSE, "%3d%%", percentage);
250 }
251
252 static void
253 print_download_speed (struct dot_progress *dp, long bytes, long dltime)
254 {
255   logprintf (LOG_VERBOSE, " %s",
256              retr_rate (bytes, dltime - dp->last_timer_value, 1));
257   dp->last_timer_value = dltime;
258 }
259
260 /* Dot-progress backend for progress_update. */
261
262 static void
263 dot_update (void *progress, long howmuch, long dltime)
264 {
265   struct dot_progress *dp = progress;
266   int dot_bytes = opt.dot_bytes;
267   long row_bytes = opt.dot_bytes * opt.dots_in_line;
268
269   log_set_flush (0);
270
271   dp->accumulated += howmuch;
272   for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
273     {
274       if (dp->dots == 0)
275         logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
276
277       if (dp->dots % opt.dot_spacing == 0)
278         logputs (LOG_VERBOSE, " ");
279       logputs (LOG_VERBOSE, ".");
280
281       ++dp->dots;
282       if (dp->dots >= opt.dots_in_line)
283         {
284           long row_qty = row_bytes;
285           if (dp->rows == dp->initial_length / row_bytes)
286             row_qty -= dp->initial_length % row_bytes;
287
288           ++dp->rows;
289           dp->dots = 0;
290
291           if (dp->total_length)
292             print_percentage (dp->rows * row_bytes, dp->total_length);
293           print_download_speed (dp, row_qty, dltime);
294         }
295     }
296
297   log_set_flush (1);
298 }
299
300 /* Dot-progress backend for progress_finish. */
301
302 static void
303 dot_finish (void *progress, long dltime)
304 {
305   struct dot_progress *dp = progress;
306   int dot_bytes = opt.dot_bytes;
307   long row_bytes = opt.dot_bytes * opt.dots_in_line;
308   int i;
309
310   log_set_flush (0);
311
312   if (dp->dots == 0)
313     logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
314   for (i = dp->dots; i < opt.dots_in_line; i++)
315     {
316       if (i % opt.dot_spacing == 0)
317         logputs (LOG_VERBOSE, " ");
318       logputs (LOG_VERBOSE, " ");
319     }
320   if (dp->total_length)
321     {
322       print_percentage (dp->rows * row_bytes
323                         + dp->dots * dot_bytes
324                         + dp->accumulated,
325                         dp->total_length);
326     }
327
328   {
329     long row_qty = dp->dots * dot_bytes + dp->accumulated;
330     if (dp->rows == dp->initial_length / row_bytes)
331       row_qty -= dp->initial_length % row_bytes;
332     print_download_speed (dp, row_qty, dltime);
333   }
334
335   logputs (LOG_VERBOSE, "\n\n");
336   log_set_flush (0);
337
338   xfree (dp);
339 }
340
341 /* This function interprets the progress "parameters".  For example,
342    if Wget is invoked with --progress=dot:mega, it will set the
343    "dot-style" to "mega".  Valid styles are default, binary, mega, and
344    giga.  */
345
346 static void
347 dot_set_params (const char *params)
348 {
349   if (!params || !*params)
350     params = opt.dot_style;
351
352   if (!params)
353     return;
354
355   /* We use this to set the retrieval style.  */
356   if (!strcasecmp (params, "default"))
357     {
358       /* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
359          line.  */
360       opt.dot_bytes = 1024;
361       opt.dot_spacing = 10;
362       opt.dots_in_line = 50;
363     }
364   else if (!strcasecmp (params, "binary"))
365     {
366       /* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
367          (384K) in a line.  */
368       opt.dot_bytes = 8192;
369       opt.dot_spacing = 16;
370       opt.dots_in_line = 48;
371     }
372   else if (!strcasecmp (params, "mega"))
373     {
374       /* "Mega" retrieval, for retrieving very long files; each dot is
375          64K, 8 dots in a cluster, 6 clusters (3M) in a line.  */
376       opt.dot_bytes = 65536L;
377       opt.dot_spacing = 8;
378       opt.dots_in_line = 48;
379     }
380   else if (!strcasecmp (params, "giga"))
381     {
382       /* "Giga" retrieval, for retrieving very very *very* long files;
383          each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
384          line.  */
385       opt.dot_bytes = (1L << 20);
386       opt.dot_spacing = 8;
387       opt.dots_in_line = 32;
388     }
389   else
390     fprintf (stderr,
391              _("Invalid dot style specification `%s'; leaving unchanged.\n"),
392              params);
393 }
394 \f
395 /* "Thermometer" (bar) progress. */
396
397 /* Assumed screen width if we can't find the real value.  */
398 #define DEFAULT_SCREEN_WIDTH 80
399
400 /* Minimum screen width we'll try to work with.  If this is too small,
401    create_image will overflow the buffer.  */
402 #define MINIMUM_SCREEN_WIDTH 45
403
404 /* Number of recent packets we keep the stats for. */
405 #define RECENT_ARRAY_SIZE 30
406
407 static int screen_width = DEFAULT_SCREEN_WIDTH;
408
409 struct bar_progress {
410   long initial_length;          /* how many bytes have been downloaded
411                                    previously. */
412   long total_length;            /* expected total byte count when the
413                                    download finishes */
414   long count;                   /* bytes downloaded so far */
415
416   long last_screen_update;      /* time of the last screen update. */
417
418   int width;                    /* screen width we're using at the
419                                    time the progress gauge was
420                                    created.  this is different from
421                                    the screen_width global variable in
422                                    that the latter can be changed by a
423                                    signal. */
424   char *buffer;                 /* buffer where the bar "image" is
425                                    stored. */
426   int tick;                     /* counter used for drawing the
427                                    progress bar where the total size
428                                    is not known. */
429
430   /* The following variables (kept in a struct for namespace reasons)
431      keep track of how long it took to read recent packets.  See
432      bar_update() for explanation.  */
433   struct {
434     long previous_time;
435     long times[RECENT_ARRAY_SIZE];
436     long bytes[RECENT_ARRAY_SIZE];
437     int count;
438     long summed_times;
439     long summed_bytes;
440   } recent;
441
442   /* create_image() uses these to make sure that ETA information
443      doesn't flash. */
444   long last_eta_time;           /* time of the last update to download
445                                    speed and ETA. */
446   long last_eta_value;
447 };
448
449 static void create_image PARAMS ((struct bar_progress *, long));
450 static void display_image PARAMS ((char *));
451
452 static void *
453 bar_create (long initial, long total)
454 {
455   struct bar_progress *bp = xmalloc (sizeof (struct bar_progress));
456
457   memset (bp, 0, sizeof (*bp));
458
459   bp->initial_length = initial;
460   bp->total_length   = total;
461
462   /* - 1 because we don't want to use the last screen column. */
463   bp->width = screen_width - 1;
464   /* + 1 for the terminating zero. */
465   bp->buffer = xmalloc (bp->width + 1);
466
467   logputs (LOG_VERBOSE, "\n");
468
469   create_image (bp, 0);
470   display_image (bp->buffer);
471
472   return bp;
473 }
474
475 static void
476 bar_update (void *progress, long howmuch, long dltime)
477 {
478   struct bar_progress *bp = progress;
479   int force_screen_update = 0;
480   int rec_index;
481
482   bp->count += howmuch;
483   if (bp->total_length > 0
484       && bp->count + bp->initial_length > bp->total_length)
485     /* We could be downloading more than total_length, e.g. when the
486        server sends an incorrect Content-Length header.  In that case,
487        adjust bp->total_length to the new reality, so that the code in
488        create_image() that depends on total size being smaller or
489        equal to the expected size doesn't abort.  */
490     bp->total_length = bp->count + bp->initial_length;
491
492   /* The progress bar is supposed to display the "current download
493      speed".  The first version of the progress bar calculated it by
494      dividing the total amount of data with the total time needed to
495      download it.  The problem with this was that stalled or suspended
496      download could unduly influence the "current" time.  Taking just
497      the time needed to download the current packet would not work
498      either because packets arrive too fast and the varitions would be
499      too jerky.
500
501      It would be preferrable to show the speed that pertains to a
502      recent period, say over the past several seconds.  But to do this
503      accurately, we would have to record all the packets received
504      during the last five seconds.
505
506      What we do instead is maintain a history of a fixed number of
507      packets.  It actually makes sense if you think about it -- faster
508      downloads will have a faster response to speed changes.  */
509
510   rec_index = bp->recent.count % RECENT_ARRAY_SIZE;
511   ++bp->recent.count;
512
513   /* Instead of calculating the sum of times[] and bytes[], we
514      maintain the summed quantities.  To maintain each sum, we must
515      make sure that it gets increased by the newly downloaded amount,
516      but also that it gets decreased by the amount we're overwriting
517      in (erasing from) the cyclical buffer.  */
518   bp->recent.summed_times -= bp->recent.times[rec_index];
519   bp->recent.summed_bytes -= bp->recent.bytes[rec_index];
520
521   bp->recent.times[rec_index] = dltime - bp->recent.previous_time;
522   bp->recent.bytes[rec_index] = howmuch;
523
524   bp->recent.summed_times += bp->recent.times[rec_index];
525   bp->recent.summed_bytes += bp->recent.bytes[rec_index];
526
527   bp->recent.previous_time = dltime;
528
529 #if 0
530   /* Sledgehammer check that summed_times and summed_bytes are
531      accurate.  */
532   {
533     int num = bp->recent.count;
534     int i;
535     int upper = num < RECENT_ARRAY_SIZE ? num : RECENT_ARRAY_SIZE;
536     long sumt = 0, sumb = 0;
537     for (i = 0; i < upper; i++)
538       {
539         sumt += bp->recent.times[i];
540         sumb += bp->recent.bytes[i];
541       }
542     assert (sumt == bp->recent.summed_times);
543     assert (sumb == bp->recent.summed_bytes);
544   }
545 #endif
546
547   if (screen_width - 1 != bp->width)
548     {
549       bp->width = screen_width - 1;
550       bp->buffer = xrealloc (bp->buffer, bp->width + 1);
551       force_screen_update = 1;
552     }
553
554   if (dltime - bp->last_screen_update < 200 && !force_screen_update)
555     /* Don't update more often than five times per second. */
556     return;
557
558   create_image (bp, dltime);
559   display_image (bp->buffer);
560   bp->last_screen_update = dltime;
561 }
562
563 static void
564 bar_finish (void *progress, long dltime)
565 {
566   struct bar_progress *bp = progress;
567
568   if (dltime == 0)
569     /* If the download was faster than the granularity of the timer,
570        fake some output so that we don't get the ugly "----.--" rate
571        at the download finish.  */
572     dltime = 1;
573
574   create_image (bp, dltime);
575   display_image (bp->buffer);
576
577   logputs (LOG_VERBOSE, "\n\n");
578
579   xfree (bp->buffer);
580   xfree (bp);
581 }
582
583 #define APPEND_LITERAL(s) do {                  \
584   memcpy (p, s, sizeof (s) - 1);                \
585   p += sizeof (s) - 1;                          \
586 } while (0)
587
588 #ifndef MAX
589 # define MAX(a, b) ((a) >= (b) ? (a) : (b))
590 #endif
591
592 static void
593 create_image (struct bar_progress *bp, long dl_total_time)
594 {
595   char *p = bp->buffer;
596   long size = bp->initial_length + bp->count;
597
598   char *size_legible = legible (size);
599   int size_legible_len = strlen (size_legible);
600
601   long recent_time = bp->recent.summed_times;
602   long recent_bytes = bp->recent.summed_bytes;
603
604   /* The progress bar should look like this:
605      xx% [=======>             ] nn,nnn 12.34K/s ETA 00:00
606
607      Calculate the geometry.  The idea is to assign as much room as
608      possible to the progress bar.  The other idea is to never let
609      things "jitter", i.e. pad elements that vary in size so that
610      their variance does not affect the placement of other elements.
611      It would be especially bad for the progress bar to be resized
612      randomly.
613
614      "xx% " or "100%"  - percentage               - 4 chars
615      "[]"              - progress bar decorations - 2 chars
616      " nnn,nnn,nnn"    - downloaded bytes         - 12 chars or very rarely more
617      " 1012.56K/s"     - dl rate                  - 11 chars
618      " ETA xx:xx:xx"   - ETA                      - 13 chars
619
620      "=====>..."       - progress bar             - the rest
621   */
622   int dlbytes_size = 1 + MAX (size_legible_len, 11);
623   int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);
624
625   if (progress_size < 5)
626     progress_size = 0;
627
628   /* "xx% " */
629   if (bp->total_length > 0)
630     {
631       int percentage = (int)(100.0 * size / bp->total_length);
632
633       assert (percentage <= 100);
634
635       if (percentage < 100)
636         sprintf (p, "%2d%% ", percentage);
637       else
638         strcpy (p, "100%");
639       p += 4;
640     }
641   else
642     APPEND_LITERAL ("    ");
643
644   /* The progress bar: "[====>      ]" */
645   if (progress_size && bp->total_length > 0)
646     {
647       double fraction = (double)size / bp->total_length;
648       int dlsz = (int)(fraction * progress_size);
649       char *begin;
650
651       assert (dlsz <= progress_size);
652
653       *p++ = '[';
654       begin = p;
655
656       if (dlsz > 0)
657         {
658           /* Draw dlsz-1 '=' chars and one arrow char.  */
659           while (dlsz-- > 1)
660             *p++ = '=';
661           *p++ = '>';
662         }
663
664       while (p - begin < progress_size)
665         *p++ = ' ';
666
667       *p++ = ']';
668     }
669   else if (progress_size)
670     {
671       /* If we can't draw a real progress bar, then at least show
672          *something* to the user.  */
673       int ind = bp->tick % (progress_size * 2 - 6);
674       int i, pos;
675
676       /* Make the star move in two directions. */
677       if (ind < progress_size - 2)
678         pos = ind + 1;
679       else
680         pos = progress_size - (ind - progress_size + 5);
681
682       *p++ = '[';
683       for (i = 0; i < progress_size; i++)
684         {
685           if      (i == pos - 1) *p++ = '<';
686           else if (i == pos    ) *p++ = '=';
687           else if (i == pos + 1) *p++ = '>';
688           else
689             *p++ = ' ';
690         }
691       *p++ = ']';
692
693       ++bp->tick;
694     }
695
696   /* " 234,567,890" */
697   sprintf (p, " %-11s", legible (size));
698   p += strlen (p);
699
700   /* " 1012.45K/s" */
701   if (recent_time && recent_bytes)
702     {
703       static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
704       int units = 0;
705       double dlrate = calc_rate (recent_bytes, recent_time, &units);
706       sprintf (p, " %7.2f%s", dlrate, short_units[units]);
707       p += strlen (p);
708     }
709   else
710     APPEND_LITERAL ("   --.--K/s");
711
712   /* " ETA xx:xx:xx" */
713   if (bp->total_length > 0 && recent_bytes > 0)
714     {
715       long eta;
716       int eta_hrs, eta_min, eta_sec;
717
718       /* Don't change the value of ETA more than approximately once
719          per second; doing so would cause flashing without providing
720          any value to the user.  */
721       if (dl_total_time - bp->last_eta_time < 900
722           && bp->last_eta_value != 0)
723         eta = bp->last_eta_value;
724       else
725         {
726           double tm_sofar = (double)recent_time / 1000;
727           long bytes_remaining = bp->total_length - size;
728           eta = (long) (tm_sofar * bytes_remaining / recent_bytes);
729           bp->last_eta_value = eta;
730           bp->last_eta_time = dl_total_time;
731         }
732
733       eta_hrs = eta / 3600, eta %= 3600;
734       eta_min = eta / 60,   eta %= 60;
735       eta_sec = eta;
736
737       /* Pad until the end of screen.  The padding is dependent on the
738          hour value.  */
739       if (eta_hrs == 0 || eta_hrs > 99)
740         /* Hours not printed: pad with three spaces (two digits and
741            colon). */
742         APPEND_LITERAL ("   ");
743       else if (eta_hrs < 10)
744         /* Hours printed with one digit: pad with one space. */
745         *p++ = ' ';
746       else
747         /* Hours printed with two digits: we're using maximum width,
748            don't pad. */
749         ;
750
751       APPEND_LITERAL (" ETA ");
752
753       if (eta_hrs > 99)
754         /* Bogus value, probably due to a calculation overflow.  Print
755            something safe to avoid overstepping the buffer bounds. */
756         sprintf (p, "--:--");
757       else if (eta_hrs > 0)
758         sprintf (p, "%d:%02d:%02d", eta_hrs, eta_min, eta_sec);
759       else
760         sprintf (p, "%02d:%02d", eta_min, eta_sec);
761       p += strlen (p);
762     }
763   else if (bp->total_length > 0)
764     APPEND_LITERAL ("    ETA --:--");
765
766   assert (p - bp->buffer <= bp->width);
767
768   while (p < bp->buffer + bp->width)
769     *p++ = ' ';
770   *p = '\0';
771 }
772
773 /* Print the contents of the buffer as a one-line ASCII "image" so
774    that it can be overwritten next time.  */
775
776 static void
777 display_image (char *buf)
778 {
779   int old = log_set_save_context (0);
780   logputs (LOG_VERBOSE, "\r");
781   logputs (LOG_VERBOSE, buf);
782   log_set_save_context (old);
783 }
784
785 static void
786 bar_set_params (const char *params)
787 {
788   int sw;
789
790   if (params
791       && 0 == strcmp (params, "force"))
792     current_impl_locked = 1;
793
794   if ((opt.lfilename
795 #ifdef HAVE_ISATTY
796        || !isatty (fileno (stderr))
797 #else
798        1
799 #endif
800        )
801       && !current_impl_locked)
802     {
803       /* We're not printing to a TTY, so revert to the fallback
804          display.  #### We're recursively calling
805          set_progress_implementation here, which is slightly kludgy.
806          It would be nicer if we provided that function a return value
807          indicating a failure of some sort.  */
808       set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
809       return;
810     }
811
812   sw = determine_screen_width ();
813   if (sw && sw >= MINIMUM_SCREEN_WIDTH)
814     screen_width = sw;
815 }
816
817 #ifdef SIGWINCH
818 RETSIGTYPE
819 progress_handle_sigwinch (int sig)
820 {
821   int sw = determine_screen_width ();
822   if (sw && sw >= MINIMUM_SCREEN_WIDTH)
823     screen_width = sw;
824   signal (SIGWINCH, progress_handle_sigwinch);
825 }
826 #endif