]> sjero.net Git - wget/blob - src/progress.c
[svn] Update copyright years.
[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 #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) PARAMS ((long, long));
45   void (*update) PARAMS ((void *, long, long));
46   void (*finish) PARAMS ((void *, long));
47   void (*set_params) 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   /* In theory, our callers should take care of this pathological
465      case, but it can sometimes happen. */
466   if (initial > total)
467     total = initial;
468
469   bp->initial_length = initial;
470   bp->total_length   = total;
471
472   /* - 1 because we don't want to use the last screen column. */
473   bp->width = screen_width - 1;
474   /* + 1 for the terminating zero. */
475   bp->buffer = xmalloc (bp->width + 1);
476
477   logputs (LOG_VERBOSE, "\n");
478
479   create_image (bp, 0);
480   display_image (bp->buffer);
481
482   return bp;
483 }
484
485 static void
486 bar_update (void *progress, long howmuch, long dltime)
487 {
488   struct bar_progress *bp = progress;
489   struct bar_progress_hist *hist = &bp->hist;
490   int force_screen_update = 0;
491   long delta_time = dltime - hist->previous_time;
492
493   bp->count += howmuch;
494   if (bp->total_length > 0
495       && bp->count + bp->initial_length > bp->total_length)
496     /* We could be downloading more than total_length, e.g. when the
497        server sends an incorrect Content-Length header.  In that case,
498        adjust bp->total_length to the new reality, so that the code in
499        create_image() that depends on total size being smaller or
500        equal to the expected size doesn't abort.  */
501     bp->total_length = bp->initial_length + bp->count;
502
503   /* This code attempts to determine the current download speed.  We
504      measure the speed over the interval of approximately three
505      seconds, in subintervals no smaller than 0.1s.  In other words,
506      we maintain and use the history of 30 most recent reads, where a
507      "read" consists of one or more network reads, up until the point
508      where a subinterval is filled. */
509
510   if (hist->times[hist->pos]
511       >= DLSPEED_HISTORY_MAX_INTERVAL / DLSPEED_HISTORY_SIZE)
512     {
513       /* The subinterval at POS has been used up.  Move on to the next
514          position. */
515       if (++hist->pos == DLSPEED_HISTORY_SIZE)
516         hist->pos = 0;
517
518       /* Invalidate old data (from the previous cycle) at this
519          position. */
520       hist->summed_times -= hist->times[hist->pos];
521       hist->summed_bytes -= hist->bytes[hist->pos];
522       hist->times[hist->pos] = delta_time;
523       hist->bytes[hist->pos] = howmuch;
524     }
525   else
526     {
527       /* Increment the data at POS. */
528       hist->times[hist->pos] += delta_time;
529       hist->bytes[hist->pos] += howmuch;
530     }
531
532   hist->summed_times += delta_time;
533   hist->summed_bytes += howmuch;
534   hist->previous_time = dltime;
535
536 #if 0
537   /* Sledgehammer check that summed_times and summed_bytes are
538      accurate.  */
539   {
540     int i;
541     long sumt = 0, sumb = 0;
542     for (i = 0; i < DLSPEED_HISTORY_SIZE; i++)
543       {
544         sumt += hist->times[i];
545         sumb += hist->bytes[i];
546       }
547     assert (sumt == hist->summed_times);
548     assert (sumb == hist->summed_bytes);
549   }
550 #endif
551
552   if (screen_width - 1 != bp->width)
553     {
554       bp->width = screen_width - 1;
555       bp->buffer = xrealloc (bp->buffer, bp->width + 1);
556       force_screen_update = 1;
557     }
558
559   if (dltime - bp->last_screen_update < 200 && !force_screen_update)
560     /* Don't update more often than five times per second. */
561     return;
562
563   create_image (bp, dltime);
564   display_image (bp->buffer);
565   bp->last_screen_update = dltime;
566 }
567
568 static void
569 bar_finish (void *progress, long dltime)
570 {
571   struct bar_progress *bp = progress;
572
573   if (bp->total_length > 0
574       && bp->count + bp->initial_length > bp->total_length)
575     /* See bar_update() for explanation. */
576     bp->total_length = bp->initial_length + bp->count;
577
578   create_image (bp, dltime);
579   display_image (bp->buffer);
580
581   logputs (LOG_VERBOSE, "\n\n");
582
583   xfree (bp->buffer);
584   xfree (bp);
585 }
586
587 #define APPEND_LITERAL(s) do {                  \
588   memcpy (p, s, sizeof (s) - 1);                \
589   p += sizeof (s) - 1;                          \
590 } while (0)
591
592 #ifndef MAX
593 # define MAX(a, b) ((a) >= (b) ? (a) : (b))
594 #endif
595
596 static void
597 create_image (struct bar_progress *bp, long dl_total_time)
598 {
599   char *p = bp->buffer;
600   long size = bp->initial_length + bp->count;
601
602   char *size_legible = legible (size);
603   int size_legible_len = strlen (size_legible);
604
605   struct bar_progress_hist *hist = &bp->hist;
606
607   /* The progress bar should look like this:
608      xx% [=======>             ] nn,nnn 12.34K/s ETA 00:00
609
610      Calculate the geometry.  The idea is to assign as much room as
611      possible to the progress bar.  The other idea is to never let
612      things "jitter", i.e. pad elements that vary in size so that
613      their variance does not affect the placement of other elements.
614      It would be especially bad for the progress bar to be resized
615      randomly.
616
617      "xx% " or "100%"  - percentage               - 4 chars
618      "[]"              - progress bar decorations - 2 chars
619      " nnn,nnn,nnn"    - downloaded bytes         - 12 chars or very rarely more
620      " 1012.56K/s"     - dl rate                  - 11 chars
621      " ETA xx:xx:xx"   - ETA                      - 13 chars
622
623      "=====>..."       - progress bar             - the rest
624   */
625   int dlbytes_size = 1 + MAX (size_legible_len, 11);
626   int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);
627
628   if (progress_size < 5)
629     progress_size = 0;
630
631   /* "xx% " */
632   if (bp->total_length > 0)
633     {
634       int percentage = (int)(100.0 * size / bp->total_length);
635
636       assert (percentage <= 100);
637
638       if (percentage < 100)
639         sprintf (p, "%2d%% ", percentage);
640       else
641         strcpy (p, "100%");
642       p += 4;
643     }
644   else
645     APPEND_LITERAL ("    ");
646
647   /* The progress bar: "[====>      ]" */
648   if (progress_size && bp->total_length > 0)
649     {
650       double fraction = (double)size / bp->total_length;
651       int dlsz = (int)(fraction * progress_size);
652       char *begin;
653
654       assert (dlsz <= progress_size);
655
656       *p++ = '[';
657       begin = p;
658
659       if (dlsz > 0)
660         {
661           /* Draw dlsz-1 '=' chars and one arrow char.  */
662           while (dlsz-- > 1)
663             *p++ = '=';
664           *p++ = '>';
665         }
666
667       while (p - begin < progress_size)
668         *p++ = ' ';
669
670       *p++ = ']';
671     }
672   else if (progress_size)
673     {
674       /* If we can't draw a real progress bar, then at least show
675          *something* to the user.  */
676       int ind = bp->tick % (progress_size * 2 - 6);
677       int i, pos;
678
679       /* Make the star move in two directions. */
680       if (ind < progress_size - 2)
681         pos = ind + 1;
682       else
683         pos = progress_size - (ind - progress_size + 5);
684
685       *p++ = '[';
686       for (i = 0; i < progress_size; i++)
687         {
688           if      (i == pos - 1) *p++ = '<';
689           else if (i == pos    ) *p++ = '=';
690           else if (i == pos + 1) *p++ = '>';
691           else
692             *p++ = ' ';
693         }
694       *p++ = ']';
695
696       ++bp->tick;
697     }
698
699   /* " 234,567,890" */
700   sprintf (p, " %-11s", legible (size));
701   p += strlen (p);
702
703   /* " 1012.45K/s" */
704   if (hist->summed_times && hist->summed_bytes)
705     {
706       static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
707       int units = 0;
708       double dlrate;
709       dlrate = calc_rate (hist->summed_bytes, hist->summed_times, &units);
710       sprintf (p, " %7.2f%s", dlrate, short_units[units]);
711       p += strlen (p);
712     }
713   else
714     APPEND_LITERAL ("   --.--K/s");
715
716   /* " ETA xx:xx:xx" */
717   if (bp->total_length > 0 && dl_total_time > 3000)
718     {
719       long eta;
720       int eta_hrs, eta_min, eta_sec;
721
722       /* Don't change the value of ETA more than approximately once
723          per second; doing so would cause flashing without providing
724          any value to the user. */
725       if (dl_total_time - bp->last_eta_time < 900
726           && bp->last_eta_value != 0)
727         eta = bp->last_eta_value;
728       else
729         {
730           /* Calculate ETA using the average download speed to predict
731              the future speed.  If you want to use the current speed
732              instead, replace dl_total_time with hist->summed_times
733              and bp->count with hist->summed_bytes.  I found that
734              doing that results in a very jerky and ultimately
735              unreliable ETA.  */
736           double time_sofar = (double)dl_total_time / 1000;
737           long bytes_remaining = bp->total_length - size;
738           eta = (long) (time_sofar * bytes_remaining / bp->count);
739           bp->last_eta_value = eta;
740           bp->last_eta_time = dl_total_time;
741         }
742
743       eta_hrs = eta / 3600, eta %= 3600;
744       eta_min = eta / 60,   eta %= 60;
745       eta_sec = eta;
746
747       if (eta_hrs > 99)
748         goto no_eta;
749
750       if (eta_hrs == 0)
751         {
752           /* Hours not printed: pad with three spaces. */
753           APPEND_LITERAL ("   ");
754           sprintf (p, " ETA %02d:%02d", eta_min, eta_sec);
755         }
756       else
757         {
758           if (eta_hrs < 10)
759             /* Hours printed with one digit: pad with one space. */
760             *p++ = ' ';
761           sprintf (p, " ETA %d:%02d:%02d", eta_hrs, eta_min, eta_sec);
762         }
763       p += strlen (p);
764     }
765   else if (bp->total_length > 0)
766     {
767     no_eta:
768       APPEND_LITERAL ("             ");
769     }
770
771   assert (p - bp->buffer <= bp->width);
772
773   while (p < bp->buffer + bp->width)
774     *p++ = ' ';
775   *p = '\0';
776 }
777
778 /* Print the contents of the buffer as a one-line ASCII "image" so
779    that it can be overwritten next time.  */
780
781 static void
782 display_image (char *buf)
783 {
784   int old = log_set_save_context (0);
785   logputs (LOG_VERBOSE, "\r");
786   logputs (LOG_VERBOSE, buf);
787   log_set_save_context (old);
788 }
789
790 static void
791 bar_set_params (const char *params)
792 {
793   int sw;
794
795   if (params
796       && 0 == strcmp (params, "force"))
797     current_impl_locked = 1;
798
799   if ((opt.lfilename
800 #ifdef HAVE_ISATTY
801        || !isatty (fileno (stderr))
802 #else
803        1
804 #endif
805        )
806       && !current_impl_locked)
807     {
808       /* We're not printing to a TTY, so revert to the fallback
809          display.  #### We're recursively calling
810          set_progress_implementation here, which is slightly kludgy.
811          It would be nicer if we provided that function a return value
812          indicating a failure of some sort.  */
813       set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
814       return;
815     }
816
817   sw = determine_screen_width ();
818   if (sw && sw >= MINIMUM_SCREEN_WIDTH)
819     screen_width = sw;
820 }
821
822 #ifdef SIGWINCH
823 RETSIGTYPE
824 progress_handle_sigwinch (int sig)
825 {
826   int sw = determine_screen_width ();
827   if (sw && sw >= MINIMUM_SCREEN_WIDTH)
828     screen_width = sw;
829   signal (SIGWINCH, progress_handle_sigwinch);
830 }
831 #endif