]> sjero.net Git - wget/blob - src/progress.c
[svn] Update the license to include the OpenSSL exception.
[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, long));
56   void (*finish) PARAMS ((void *, long));
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, long));
64 static void dot_finish PARAMS ((void *, long));
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, long));
69 static void bar_finish PARAMS ((void *, long));
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 < ARRAY_SIZE (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 < ARRAY_SIZE (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, long 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, long 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   long 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 = xmalloc (sizeof (struct dot_progress));
210
211   memset (dp, 0, sizeof (*dp));
212
213   dp->initial_length = initial;
214   dp->total_length   = total;
215
216   if (dp->initial_length)
217     {
218       int dot_bytes = opt.dot_bytes;
219       long row_bytes = opt.dot_bytes * opt.dots_in_line;
220
221       int remainder = (int) (dp->initial_length % row_bytes);
222       long skipped = dp->initial_length - remainder;
223
224       if (skipped)
225         {
226           int skipped_k = (int) (skipped / 1024); /* skipped amount in K */
227           int skipped_k_len = numdigit (skipped_k);
228           if (skipped_k_len < 5)
229             skipped_k_len = 5;
230
231           /* Align the [ skipping ... ] line with the dots.  To do
232              that, insert the number of spaces equal to the number of
233              digits in the skipped amount in K.  */
234           logprintf (LOG_VERBOSE, _("\n%*s[ skipping %dK ]"),
235                      2 + skipped_k_len, "", skipped_k);
236         }
237
238       logprintf (LOG_VERBOSE, "\n%5ldK", skipped / 1024);
239       for (; remainder >= dot_bytes; remainder -= dot_bytes)
240         {
241           if (dp->dots % opt.dot_spacing == 0)
242             logputs (LOG_VERBOSE, " ");
243           logputs (LOG_VERBOSE, ",");
244           ++dp->dots;
245         }
246       assert (dp->dots < opt.dots_in_line);
247
248       dp->accumulated = remainder;
249       dp->rows = skipped / row_bytes;
250     }
251
252   return dp;
253 }
254
255 static void
256 print_percentage (long bytes, long expected)
257 {
258   int percentage = (int)(100.0 * bytes / expected);
259   logprintf (LOG_VERBOSE, "%3d%%", percentage);
260 }
261
262 static void
263 print_download_speed (struct dot_progress *dp, long bytes, long dltime)
264 {
265   logprintf (LOG_VERBOSE, " %s",
266              retr_rate (bytes, dltime - dp->last_timer_value, 1));
267   dp->last_timer_value = dltime;
268 }
269
270 /* Dot-progress backend for progress_update. */
271
272 static void
273 dot_update (void *progress, long howmuch, long dltime)
274 {
275   struct dot_progress *dp = progress;
276   int dot_bytes = opt.dot_bytes;
277   long row_bytes = opt.dot_bytes * opt.dots_in_line;
278
279   log_set_flush (0);
280
281   dp->accumulated += howmuch;
282   for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
283     {
284       if (dp->dots == 0)
285         logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
286
287       if (dp->dots % opt.dot_spacing == 0)
288         logputs (LOG_VERBOSE, " ");
289       logputs (LOG_VERBOSE, ".");
290
291       ++dp->dots;
292       if (dp->dots >= opt.dots_in_line)
293         {
294           long row_qty = row_bytes;
295           if (dp->rows == dp->initial_length / row_bytes)
296             row_qty -= dp->initial_length % row_bytes;
297
298           ++dp->rows;
299           dp->dots = 0;
300
301           if (dp->total_length)
302             print_percentage (dp->rows * row_bytes, dp->total_length);
303           print_download_speed (dp, row_qty, dltime);
304         }
305     }
306
307   log_set_flush (1);
308 }
309
310 /* Dot-progress backend for progress_finish. */
311
312 static void
313 dot_finish (void *progress, long dltime)
314 {
315   struct dot_progress *dp = progress;
316   int dot_bytes = opt.dot_bytes;
317   long row_bytes = opt.dot_bytes * opt.dots_in_line;
318   int i;
319
320   log_set_flush (0);
321
322   if (dp->dots == 0)
323     logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
324   for (i = dp->dots; i < opt.dots_in_line; i++)
325     {
326       if (i % opt.dot_spacing == 0)
327         logputs (LOG_VERBOSE, " ");
328       logputs (LOG_VERBOSE, " ");
329     }
330   if (dp->total_length)
331     {
332       print_percentage (dp->rows * row_bytes
333                         + dp->dots * dot_bytes
334                         + dp->accumulated,
335                         dp->total_length);
336     }
337
338   {
339     long row_qty = dp->dots * dot_bytes + dp->accumulated;
340     if (dp->rows == dp->initial_length / row_bytes)
341       row_qty -= dp->initial_length % row_bytes;
342     print_download_speed (dp, row_qty, dltime);
343   }
344
345   logputs (LOG_VERBOSE, "\n\n");
346   log_set_flush (0);
347
348   xfree (dp);
349 }
350
351 /* This function interprets the progress "parameters".  For example,
352    if Wget is invoked with --progress=dot:mega, it will set the
353    "dot-style" to "mega".  Valid styles are default, binary, mega, and
354    giga.  */
355
356 static void
357 dot_set_params (const char *params)
358 {
359   if (!params || !*params)
360     params = opt.dot_style;
361
362   if (!params)
363     return;
364
365   /* We use this to set the retrieval style.  */
366   if (!strcasecmp (params, "default"))
367     {
368       /* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
369          line.  */
370       opt.dot_bytes = 1024;
371       opt.dot_spacing = 10;
372       opt.dots_in_line = 50;
373     }
374   else if (!strcasecmp (params, "binary"))
375     {
376       /* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
377          (384K) in a line.  */
378       opt.dot_bytes = 8192;
379       opt.dot_spacing = 16;
380       opt.dots_in_line = 48;
381     }
382   else if (!strcasecmp (params, "mega"))
383     {
384       /* "Mega" retrieval, for retrieving very long files; each dot is
385          64K, 8 dots in a cluster, 6 clusters (3M) in a line.  */
386       opt.dot_bytes = 65536L;
387       opt.dot_spacing = 8;
388       opt.dots_in_line = 48;
389     }
390   else if (!strcasecmp (params, "giga"))
391     {
392       /* "Giga" retrieval, for retrieving very very *very* long files;
393          each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
394          line.  */
395       opt.dot_bytes = (1L << 20);
396       opt.dot_spacing = 8;
397       opt.dots_in_line = 32;
398     }
399   else
400     fprintf (stderr,
401              _("Invalid dot style specification `%s'; leaving unchanged.\n"),
402              params);
403 }
404 \f
405 /* "Thermometer" (bar) progress. */
406
407 /* Assumed screen width if we can't find the real value.  */
408 #define DEFAULT_SCREEN_WIDTH 80
409
410 /* Minimum screen width we'll try to work with.  If this is too small,
411    create_image will overflow the buffer.  */
412 #define MINIMUM_SCREEN_WIDTH 45
413
414 static int screen_width = DEFAULT_SCREEN_WIDTH;
415
416 /* Size of the history table for download speeds. */
417 #define DLSPEED_HISTORY_SIZE 30
418
419 /* The time interval in milliseconds below which we increase old
420    history entries rather than overwriting them.  That interval
421    represents the scope of the download speed history. */
422 #define DLSPEED_HISTORY_MAX_INTERVAL 3000
423
424 struct bar_progress {
425   long initial_length;          /* how many bytes have been downloaded
426                                    previously. */
427   long total_length;            /* expected total byte count when the
428                                    download finishes */
429   long count;                   /* bytes downloaded so far */
430
431   long last_screen_update;      /* time of the last screen update. */
432
433   int width;                    /* screen width we're using at the
434                                    time the progress gauge was
435                                    created.  this is different from
436                                    the screen_width global variable in
437                                    that the latter can be changed by a
438                                    signal. */
439   char *buffer;                 /* buffer where the bar "image" is
440                                    stored. */
441   int tick;                     /* counter used for drawing the
442                                    progress bar where the total size
443                                    is not known. */
444
445   /* The following variables (kept in a struct for namespace reasons)
446      keep track of recent download speeds.  See bar_update() for
447      details.  */
448   struct bar_progress_hist {
449     int pos;
450     long times[DLSPEED_HISTORY_SIZE];
451     long bytes[DLSPEED_HISTORY_SIZE];
452     long summed_times;
453     long summed_bytes;
454     long previous_time;
455   } hist;
456
457   /* create_image() uses these to make sure that ETA information
458      doesn't flash. */
459   long last_eta_time;           /* time of the last update to download
460                                    speed and ETA. */
461   long last_eta_value;
462 };
463
464 static void create_image PARAMS ((struct bar_progress *, long));
465 static void display_image PARAMS ((char *));
466
467 static void *
468 bar_create (long initial, long total)
469 {
470   struct bar_progress *bp = xmalloc (sizeof (struct bar_progress));
471
472   memset (bp, 0, sizeof (*bp));
473
474   /* In theory, our callers should take care of this pathological
475      case, but it can sometimes happen. */
476   if (initial > total)
477     total = initial;
478
479   bp->initial_length = initial;
480   bp->total_length   = total;
481
482   /* - 1 because we don't want to use the last screen column. */
483   bp->width = screen_width - 1;
484   /* + 1 for the terminating zero. */
485   bp->buffer = xmalloc (bp->width + 1);
486
487   logputs (LOG_VERBOSE, "\n");
488
489   create_image (bp, 0);
490   display_image (bp->buffer);
491
492   return bp;
493 }
494
495 static void
496 bar_update (void *progress, long howmuch, long dltime)
497 {
498   struct bar_progress *bp = progress;
499   struct bar_progress_hist *hist = &bp->hist;
500   int force_screen_update = 0;
501   long delta_time = dltime - hist->previous_time;
502
503   bp->count += howmuch;
504   if (bp->total_length > 0
505       && bp->count + bp->initial_length > bp->total_length)
506     /* We could be downloading more than total_length, e.g. when the
507        server sends an incorrect Content-Length header.  In that case,
508        adjust bp->total_length to the new reality, so that the code in
509        create_image() that depends on total size being smaller or
510        equal to the expected size doesn't abort.  */
511     bp->total_length = bp->initial_length + bp->count;
512
513   /* This code attempts to determine the current download speed.  We
514      measure the speed over the interval of approximately three
515      seconds, in subintervals no smaller than 0.1s.  In other words,
516      we maintain and use the history of 30 most recent reads, where a
517      "read" consists of one or more network reads, up until the point
518      where a subinterval is filled. */
519
520   if (hist->times[hist->pos]
521       >= DLSPEED_HISTORY_MAX_INTERVAL / DLSPEED_HISTORY_SIZE)
522     {
523       /* The subinterval at POS has been used up.  Move on to the next
524          position. */
525       if (++hist->pos == DLSPEED_HISTORY_SIZE)
526         hist->pos = 0;
527
528       /* Invalidate old data (from the previous cycle) at this
529          position. */
530       hist->summed_times -= hist->times[hist->pos];
531       hist->summed_bytes -= hist->bytes[hist->pos];
532       hist->times[hist->pos] = delta_time;
533       hist->bytes[hist->pos] = howmuch;
534     }
535   else
536     {
537       /* Increment the data at POS. */
538       hist->times[hist->pos] += delta_time;
539       hist->bytes[hist->pos] += howmuch;
540     }
541
542   hist->summed_times += delta_time;
543   hist->summed_bytes += howmuch;
544   hist->previous_time = dltime;
545
546 #if 0
547   /* Sledgehammer check that summed_times and summed_bytes are
548      accurate.  */
549   {
550     int i;
551     long sumt = 0, sumb = 0;
552     for (i = 0; i < DLSPEED_HISTORY_SIZE; i++)
553       {
554         sumt += hist->times[i];
555         sumb += hist->bytes[i];
556       }
557     assert (sumt == hist->summed_times);
558     assert (sumb == hist->summed_bytes);
559   }
560 #endif
561
562   if (screen_width - 1 != bp->width)
563     {
564       bp->width = screen_width - 1;
565       bp->buffer = xrealloc (bp->buffer, bp->width + 1);
566       force_screen_update = 1;
567     }
568
569   if (dltime - bp->last_screen_update < 200 && !force_screen_update)
570     /* Don't update more often than five times per second. */
571     return;
572
573   create_image (bp, dltime);
574   display_image (bp->buffer);
575   bp->last_screen_update = dltime;
576 }
577
578 static void
579 bar_finish (void *progress, long dltime)
580 {
581   struct bar_progress *bp = progress;
582
583   if (bp->total_length > 0
584       && bp->count + bp->initial_length > bp->total_length)
585     /* See bar_update() for explanation. */
586     bp->total_length = bp->initial_length + bp->count;
587
588   create_image (bp, dltime);
589   display_image (bp->buffer);
590
591   logputs (LOG_VERBOSE, "\n\n");
592
593   xfree (bp->buffer);
594   xfree (bp);
595 }
596
597 #define APPEND_LITERAL(s) do {                  \
598   memcpy (p, s, sizeof (s) - 1);                \
599   p += sizeof (s) - 1;                          \
600 } while (0)
601
602 #ifndef MAX
603 # define MAX(a, b) ((a) >= (b) ? (a) : (b))
604 #endif
605
606 static void
607 create_image (struct bar_progress *bp, long dl_total_time)
608 {
609   char *p = bp->buffer;
610   long size = bp->initial_length + bp->count;
611
612   char *size_legible = legible (size);
613   int size_legible_len = strlen (size_legible);
614
615   struct bar_progress_hist *hist = &bp->hist;
616
617   /* The progress bar should look like this:
618      xx% [=======>             ] nn,nnn 12.34K/s ETA 00:00
619
620      Calculate the geometry.  The idea is to assign as much room as
621      possible to the progress bar.  The other idea is to never let
622      things "jitter", i.e. pad elements that vary in size so that
623      their variance does not affect the placement of other elements.
624      It would be especially bad for the progress bar to be resized
625      randomly.
626
627      "xx% " or "100%"  - percentage               - 4 chars
628      "[]"              - progress bar decorations - 2 chars
629      " nnn,nnn,nnn"    - downloaded bytes         - 12 chars or very rarely more
630      " 1012.56K/s"     - dl rate                  - 11 chars
631      " ETA xx:xx:xx"   - ETA                      - 13 chars
632
633      "=====>..."       - progress bar             - the rest
634   */
635   int dlbytes_size = 1 + MAX (size_legible_len, 11);
636   int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);
637
638   if (progress_size < 5)
639     progress_size = 0;
640
641   /* "xx% " */
642   if (bp->total_length > 0)
643     {
644       int percentage = (int)(100.0 * size / bp->total_length);
645
646       assert (percentage <= 100);
647
648       if (percentage < 100)
649         sprintf (p, "%2d%% ", percentage);
650       else
651         strcpy (p, "100%");
652       p += 4;
653     }
654   else
655     APPEND_LITERAL ("    ");
656
657   /* The progress bar: "[====>      ]" */
658   if (progress_size && bp->total_length > 0)
659     {
660       double fraction = (double)size / bp->total_length;
661       int dlsz = (int)(fraction * progress_size);
662       char *begin;
663
664       assert (dlsz <= progress_size);
665
666       *p++ = '[';
667       begin = p;
668
669       if (dlsz > 0)
670         {
671           /* Draw dlsz-1 '=' chars and one arrow char.  */
672           while (dlsz-- > 1)
673             *p++ = '=';
674           *p++ = '>';
675         }
676
677       while (p - begin < progress_size)
678         *p++ = ' ';
679
680       *p++ = ']';
681     }
682   else if (progress_size)
683     {
684       /* If we can't draw a real progress bar, then at least show
685          *something* to the user.  */
686       int ind = bp->tick % (progress_size * 2 - 6);
687       int i, pos;
688
689       /* Make the star move in two directions. */
690       if (ind < progress_size - 2)
691         pos = ind + 1;
692       else
693         pos = progress_size - (ind - progress_size + 5);
694
695       *p++ = '[';
696       for (i = 0; i < progress_size; i++)
697         {
698           if      (i == pos - 1) *p++ = '<';
699           else if (i == pos    ) *p++ = '=';
700           else if (i == pos + 1) *p++ = '>';
701           else
702             *p++ = ' ';
703         }
704       *p++ = ']';
705
706       ++bp->tick;
707     }
708
709   /* " 234,567,890" */
710   sprintf (p, " %-11s", legible (size));
711   p += strlen (p);
712
713   /* " 1012.45K/s" */
714   if (hist->summed_times && hist->summed_bytes)
715     {
716       static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
717       int units = 0;
718       double dlrate;
719       dlrate = calc_rate (hist->summed_bytes, hist->summed_times, &units);
720       sprintf (p, " %7.2f%s", dlrate, short_units[units]);
721       p += strlen (p);
722     }
723   else
724     APPEND_LITERAL ("   --.--K/s");
725
726   /* " ETA xx:xx:xx" */
727   if (bp->total_length > 0 && dl_total_time > 3000)
728     {
729       long eta;
730       int eta_hrs, eta_min, eta_sec;
731
732       /* Don't change the value of ETA more than approximately once
733          per second; doing so would cause flashing without providing
734          any value to the user. */
735       if (dl_total_time - bp->last_eta_time < 900
736           && bp->last_eta_value != 0)
737         eta = bp->last_eta_value;
738       else
739         {
740           /* Calculate ETA using the average download speed to predict
741              the future speed.  If you want to use the current speed
742              instead, replace dl_total_time with hist->summed_times
743              and bp->count with hist->summed_bytes.  I found that
744              doing that results in a very jerky and ultimately
745              unreliable ETA.  */
746           double time_sofar = (double)dl_total_time / 1000;
747           long bytes_remaining = bp->total_length - size;
748           eta = (long) (time_sofar * bytes_remaining / bp->count);
749           bp->last_eta_value = eta;
750           bp->last_eta_time = dl_total_time;
751         }
752
753       eta_hrs = eta / 3600, eta %= 3600;
754       eta_min = eta / 60,   eta %= 60;
755       eta_sec = eta;
756
757       if (eta_hrs > 99)
758         goto no_eta;
759
760       if (eta_hrs == 0)
761         {
762           /* Hours not printed: pad with three spaces. */
763           APPEND_LITERAL ("   ");
764           sprintf (p, " ETA %02d:%02d", eta_min, eta_sec);
765         }
766       else
767         {
768           if (eta_hrs < 10)
769             /* Hours printed with one digit: pad with one space. */
770             *p++ = ' ';
771           sprintf (p, " ETA %d:%02d:%02d", eta_hrs, eta_min, eta_sec);
772         }
773       p += strlen (p);
774     }
775   else if (bp->total_length > 0)
776     {
777     no_eta:
778       APPEND_LITERAL ("             ");
779     }
780
781   assert (p - bp->buffer <= bp->width);
782
783   while (p < bp->buffer + bp->width)
784     *p++ = ' ';
785   *p = '\0';
786 }
787
788 /* Print the contents of the buffer as a one-line ASCII "image" so
789    that it can be overwritten next time.  */
790
791 static void
792 display_image (char *buf)
793 {
794   int old = log_set_save_context (0);
795   logputs (LOG_VERBOSE, "\r");
796   logputs (LOG_VERBOSE, buf);
797   log_set_save_context (old);
798 }
799
800 static void
801 bar_set_params (const char *params)
802 {
803   int sw;
804
805   if (params
806       && 0 == strcmp (params, "force"))
807     current_impl_locked = 1;
808
809   if ((opt.lfilename
810 #ifdef HAVE_ISATTY
811        || !isatty (fileno (stderr))
812 #else
813        1
814 #endif
815        )
816       && !current_impl_locked)
817     {
818       /* We're not printing to a TTY, so revert to the fallback
819          display.  #### We're recursively calling
820          set_progress_implementation here, which is slightly kludgy.
821          It would be nicer if we provided that function a return value
822          indicating a failure of some sort.  */
823       set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
824       return;
825     }
826
827   sw = determine_screen_width ();
828   if (sw && sw >= MINIMUM_SCREEN_WIDTH)
829     screen_width = sw;
830 }
831
832 #ifdef SIGWINCH
833 RETSIGTYPE
834 progress_handle_sigwinch (int sig)
835 {
836   int sw = determine_screen_width ();
837   if (sw && sw >= MINIMUM_SCREEN_WIDTH)
838     screen_width = sw;
839   signal (SIGWINCH, progress_handle_sigwinch);
840 }
841 #endif