]> sjero.net Git - wget/blob - src/progress.c
[svn] Keep updating a subinterval until it reaches or exceeds a watermark.
[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 static int screen_width = DEFAULT_SCREEN_WIDTH;
405
406 /* Size of the history table for download speeds. */
407 #define DLSPEED_HISTORY_SIZE 30
408
409 /* The time interval in milliseconds below which we increase old
410    history entries rather than overwriting them.  That interval
411    represents the scope of the download speed history. */
412 #define DLSPEED_HISTORY_MAX_INTERVAL 3000
413
414 struct bar_progress {
415   long initial_length;          /* how many bytes have been downloaded
416                                    previously. */
417   long total_length;            /* expected total byte count when the
418                                    download finishes */
419   long count;                   /* bytes downloaded so far */
420
421   long last_screen_update;      /* time of the last screen update. */
422
423   int width;                    /* screen width we're using at the
424                                    time the progress gauge was
425                                    created.  this is different from
426                                    the screen_width global variable in
427                                    that the latter can be changed by a
428                                    signal. */
429   char *buffer;                 /* buffer where the bar "image" is
430                                    stored. */
431   int tick;                     /* counter used for drawing the
432                                    progress bar where the total size
433                                    is not known. */
434
435   /* The following variables (kept in a struct for namespace reasons)
436      keep track of recent download speeds.  See bar_update() for
437      details.  */
438   struct bar_progress_hist {
439     int pos;
440     long times[DLSPEED_HISTORY_SIZE];
441     long bytes[DLSPEED_HISTORY_SIZE];
442     long summed_times;
443     long summed_bytes;
444     long previous_time;
445   } hist;
446
447   /* create_image() uses these to make sure that ETA information
448      doesn't flash. */
449   long last_eta_time;           /* time of the last update to download
450                                    speed and ETA. */
451   long last_eta_value;
452 };
453
454 static void create_image PARAMS ((struct bar_progress *, long));
455 static void display_image PARAMS ((char *));
456
457 static void *
458 bar_create (long initial, long total)
459 {
460   struct bar_progress *bp = xmalloc (sizeof (struct bar_progress));
461
462   memset (bp, 0, sizeof (*bp));
463
464   bp->initial_length = initial;
465   bp->total_length   = total;
466
467   /* - 1 because we don't want to use the last screen column. */
468   bp->width = screen_width - 1;
469   /* + 1 for the terminating zero. */
470   bp->buffer = xmalloc (bp->width + 1);
471
472   logputs (LOG_VERBOSE, "\n");
473
474   create_image (bp, 0);
475   display_image (bp->buffer);
476
477   return bp;
478 }
479
480 static void
481 bar_update (void *progress, long howmuch, long dltime)
482 {
483   struct bar_progress *bp = progress;
484   struct bar_progress_hist *hist = &bp->hist;
485   int force_screen_update = 0;
486   long delta_time = dltime - hist->previous_time;
487
488   bp->count += howmuch;
489   if (bp->total_length > 0
490       && bp->count + bp->initial_length > bp->total_length)
491     /* We could be downloading more than total_length, e.g. when the
492        server sends an incorrect Content-Length header.  In that case,
493        adjust bp->total_length to the new reality, so that the code in
494        create_image() that depends on total size being smaller or
495        equal to the expected size doesn't abort.  */
496     bp->total_length = bp->count + bp->initial_length;
497
498   /* This code attempts to determine the current download speed.  We
499      measure the speed over the interval of approximately three
500      seconds, in subintervals no smaller than 0.1s.  In other words,
501      we maintain and use the history of 30 most recent reads, where a
502      "read" consists of one or more network reads, up until the point
503      where a subinterval is filled. */
504
505   if (hist->times[hist->pos]
506       >= DLSPEED_HISTORY_MAX_INTERVAL / DLSPEED_HISTORY_SIZE)
507     {
508       /* The subinterval at POS has been used up.  Move on to the next
509          position. */
510       if (++hist->pos == DLSPEED_HISTORY_SIZE)
511         hist->pos = 0;
512
513       /* Invalidate old data (from the previous cycle) at this
514          position. */
515       hist->summed_times -= hist->times[hist->pos];
516       hist->summed_bytes -= hist->bytes[hist->pos];
517       hist->times[hist->pos] = delta_time;
518       hist->bytes[hist->pos] = howmuch;
519     }
520   else
521     {
522       /* Increment the data at POS. */
523       hist->times[hist->pos] += delta_time;
524       hist->bytes[hist->pos] += howmuch;
525     }
526
527   hist->summed_times += delta_time;
528   hist->summed_bytes += howmuch;
529   hist->previous_time = dltime;
530
531 #if 0
532   /* Sledgehammer check that summed_times and summed_bytes are
533      accurate.  */
534   {
535     int i;
536     long sumt = 0, sumb = 0;
537     for (i = 0; i < DLSPEED_HISTORY_SIZE; i++)
538       {
539         sumt += hist->times[i];
540         sumb += hist->bytes[i];
541       }
542     assert (sumt == hist->summed_times);
543     assert (sumb == hist->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 the download was faster than the granularity of the timer,
569      fake some output so that we don't get the ugly "----.--" rate at
570      the download finish.  */
571   if (bp->hist.summed_times == 0)
572     bp->hist.summed_times = 1;
573   if (dltime == 0)
574     dltime = 1;
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 #define APPEND_LITERAL(s) do {                  \
586   memcpy (p, s, sizeof (s) - 1);                \
587   p += sizeof (s) - 1;                          \
588 } while (0)
589
590 #ifndef MAX
591 # define MAX(a, b) ((a) >= (b) ? (a) : (b))
592 #endif
593
594 static void
595 create_image (struct bar_progress *bp, long dl_total_time)
596 {
597   char *p = bp->buffer;
598   long size = bp->initial_length + bp->count;
599
600   char *size_legible = legible (size);
601   int size_legible_len = strlen (size_legible);
602
603   long recent_time = bp->hist.summed_times;
604   long recent_bytes = bp->hist.summed_bytes;
605
606   /* The progress bar should look like this:
607      xx% [=======>             ] nn,nnn 12.34K/s ETA 00:00
608
609      Calculate the geometry.  The idea is to assign as much room as
610      possible to the progress bar.  The other idea is to never let
611      things "jitter", i.e. pad elements that vary in size so that
612      their variance does not affect the placement of other elements.
613      It would be especially bad for the progress bar to be resized
614      randomly.
615
616      "xx% " or "100%"  - percentage               - 4 chars
617      "[]"              - progress bar decorations - 2 chars
618      " nnn,nnn,nnn"    - downloaded bytes         - 12 chars or very rarely more
619      " 1012.56K/s"     - dl rate                  - 11 chars
620      " ETA xx:xx:xx"   - ETA                      - 13 chars
621
622      "=====>..."       - progress bar             - the rest
623   */
624   int dlbytes_size = 1 + MAX (size_legible_len, 11);
625   int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);
626
627   if (progress_size < 5)
628     progress_size = 0;
629
630   /* "xx% " */
631   if (bp->total_length > 0)
632     {
633       int percentage = (int)(100.0 * size / bp->total_length);
634
635       assert (percentage <= 100);
636
637       if (percentage < 100)
638         sprintf (p, "%2d%% ", percentage);
639       else
640         strcpy (p, "100%");
641       p += 4;
642     }
643   else
644     APPEND_LITERAL ("    ");
645
646   /* The progress bar: "[====>      ]" */
647   if (progress_size && bp->total_length > 0)
648     {
649       double fraction = (double)size / bp->total_length;
650       int dlsz = (int)(fraction * progress_size);
651       char *begin;
652
653       assert (dlsz <= progress_size);
654
655       *p++ = '[';
656       begin = p;
657
658       if (dlsz > 0)
659         {
660           /* Draw dlsz-1 '=' chars and one arrow char.  */
661           while (dlsz-- > 1)
662             *p++ = '=';
663           *p++ = '>';
664         }
665
666       while (p - begin < progress_size)
667         *p++ = ' ';
668
669       *p++ = ']';
670     }
671   else if (progress_size)
672     {
673       /* If we can't draw a real progress bar, then at least show
674          *something* to the user.  */
675       int ind = bp->tick % (progress_size * 2 - 6);
676       int i, pos;
677
678       /* Make the star move in two directions. */
679       if (ind < progress_size - 2)
680         pos = ind + 1;
681       else
682         pos = progress_size - (ind - progress_size + 5);
683
684       *p++ = '[';
685       for (i = 0; i < progress_size; i++)
686         {
687           if      (i == pos - 1) *p++ = '<';
688           else if (i == pos    ) *p++ = '=';
689           else if (i == pos + 1) *p++ = '>';
690           else
691             *p++ = ' ';
692         }
693       *p++ = ']';
694
695       ++bp->tick;
696     }
697
698   /* " 234,567,890" */
699   sprintf (p, " %-11s", legible (size));
700   p += strlen (p);
701
702   /* " 1012.45K/s" */
703   if (recent_time && recent_bytes)
704     {
705       static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
706       int units = 0;
707       double dlrate = calc_rate (recent_bytes, recent_time, &units);
708       sprintf (p, " %7.2f%s", dlrate, short_units[units]);
709       p += strlen (p);
710     }
711   else
712     APPEND_LITERAL ("   --.--K/s");
713
714   /* " ETA xx:xx:xx" */
715   if (bp->total_length > 0 && recent_bytes > 0)
716     {
717       long eta;
718       int eta_hrs, eta_min, eta_sec;
719
720       /* Don't change the value of ETA more than approximately once
721          per second; doing so would cause flashing without providing
722          any value to the user.  */
723       if (dl_total_time - bp->last_eta_time < 900
724           && bp->last_eta_value != 0)
725         eta = bp->last_eta_value;
726       else
727         {
728           double tm_sofar = (double)recent_time / 1000;
729           long bytes_remaining = bp->total_length - size;
730           eta = (long) (tm_sofar * bytes_remaining / recent_bytes);
731           bp->last_eta_value = eta;
732           bp->last_eta_time = dl_total_time;
733         }
734
735       eta_hrs = eta / 3600, eta %= 3600;
736       eta_min = eta / 60,   eta %= 60;
737       eta_sec = eta;
738
739       /* Pad until the end of screen.  The padding is dependent on the
740          hour value.  */
741       if (eta_hrs == 0 || eta_hrs > 99)
742         /* Hours not printed: pad with three spaces (two digits and
743            colon). */
744         APPEND_LITERAL ("   ");
745       else if (eta_hrs < 10)
746         /* Hours printed with one digit: pad with one space. */
747         *p++ = ' ';
748       else
749         /* Hours printed with two digits: we're using maximum width,
750            don't pad. */
751         ;
752
753       APPEND_LITERAL (" ETA ");
754
755       if (eta_hrs > 99)
756         /* Bogus value, probably due to a calculation overflow.  Print
757            something safe to avoid overstepping the buffer bounds. */
758         sprintf (p, "--:--");
759       else if (eta_hrs > 0)
760         sprintf (p, "%d:%02d:%02d", eta_hrs, eta_min, eta_sec);
761       else
762         sprintf (p, "%02d:%02d", eta_min, eta_sec);
763       p += strlen (p);
764     }
765   else if (bp->total_length > 0)
766     APPEND_LITERAL ("    ETA --:--");
767
768   assert (p - bp->buffer <= bp->width);
769
770   while (p < bp->buffer + bp->width)
771     *p++ = ' ';
772   *p = '\0';
773 }
774
775 /* Print the contents of the buffer as a one-line ASCII "image" so
776    that it can be overwritten next time.  */
777
778 static void
779 display_image (char *buf)
780 {
781   int old = log_set_save_context (0);
782   logputs (LOG_VERBOSE, "\r");
783   logputs (LOG_VERBOSE, buf);
784   log_set_save_context (old);
785 }
786
787 static void
788 bar_set_params (const char *params)
789 {
790   int sw;
791
792   if (params
793       && 0 == strcmp (params, "force"))
794     current_impl_locked = 1;
795
796   if ((opt.lfilename
797 #ifdef HAVE_ISATTY
798        || !isatty (fileno (stderr))
799 #else
800        1
801 #endif
802        )
803       && !current_impl_locked)
804     {
805       /* We're not printing to a TTY, so revert to the fallback
806          display.  #### We're recursively calling
807          set_progress_implementation here, which is slightly kludgy.
808          It would be nicer if we provided that function a return value
809          indicating a failure of some sort.  */
810       set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
811       return;
812     }
813
814   sw = determine_screen_width ();
815   if (sw && sw >= MINIMUM_SCREEN_WIDTH)
816     screen_width = sw;
817 }
818
819 #ifdef SIGWINCH
820 RETSIGTYPE
821 progress_handle_sigwinch (int sig)
822 {
823   int sw = determine_screen_width ();
824   if (sw && sw >= MINIMUM_SCREEN_WIDTH)
825     screen_width = sw;
826   signal (SIGWINCH, progress_handle_sigwinch);
827 }
828 #endif