]> sjero.net Git - wget/blob - src/progress.c
[svn] Allow Wget to be compiled with a K&R compiler.
[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) 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   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   create_image (bp, dltime);
569   display_image (bp->buffer);
570
571   logputs (LOG_VERBOSE, "\n\n");
572
573   xfree (bp->buffer);
574   xfree (bp);
575 }
576
577 #define APPEND_LITERAL(s) do {                  \
578   memcpy (p, s, sizeof (s) - 1);                \
579   p += sizeof (s) - 1;                          \
580 } while (0)
581
582 #ifndef MAX
583 # define MAX(a, b) ((a) >= (b) ? (a) : (b))
584 #endif
585
586 static void
587 create_image (struct bar_progress *bp, long dl_total_time)
588 {
589   char *p = bp->buffer;
590   long size = bp->initial_length + bp->count;
591
592   char *size_legible = legible (size);
593   int size_legible_len = strlen (size_legible);
594
595   struct bar_progress_hist *hist = &bp->hist;
596
597   /* The progress bar should look like this:
598      xx% [=======>             ] nn,nnn 12.34K/s ETA 00:00
599
600      Calculate the geometry.  The idea is to assign as much room as
601      possible to the progress bar.  The other idea is to never let
602      things "jitter", i.e. pad elements that vary in size so that
603      their variance does not affect the placement of other elements.
604      It would be especially bad for the progress bar to be resized
605      randomly.
606
607      "xx% " or "100%"  - percentage               - 4 chars
608      "[]"              - progress bar decorations - 2 chars
609      " nnn,nnn,nnn"    - downloaded bytes         - 12 chars or very rarely more
610      " 1012.56K/s"     - dl rate                  - 11 chars
611      " ETA xx:xx:xx"   - ETA                      - 13 chars
612
613      "=====>..."       - progress bar             - the rest
614   */
615   int dlbytes_size = 1 + MAX (size_legible_len, 11);
616   int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);
617
618   if (progress_size < 5)
619     progress_size = 0;
620
621   /* "xx% " */
622   if (bp->total_length > 0)
623     {
624       int percentage = (int)(100.0 * size / bp->total_length);
625
626       assert (percentage <= 100);
627
628       if (percentage < 100)
629         sprintf (p, "%2d%% ", percentage);
630       else
631         strcpy (p, "100%");
632       p += 4;
633     }
634   else
635     APPEND_LITERAL ("    ");
636
637   /* The progress bar: "[====>      ]" */
638   if (progress_size && bp->total_length > 0)
639     {
640       double fraction = (double)size / bp->total_length;
641       int dlsz = (int)(fraction * progress_size);
642       char *begin;
643
644       assert (dlsz <= progress_size);
645
646       *p++ = '[';
647       begin = p;
648
649       if (dlsz > 0)
650         {
651           /* Draw dlsz-1 '=' chars and one arrow char.  */
652           while (dlsz-- > 1)
653             *p++ = '=';
654           *p++ = '>';
655         }
656
657       while (p - begin < progress_size)
658         *p++ = ' ';
659
660       *p++ = ']';
661     }
662   else if (progress_size)
663     {
664       /* If we can't draw a real progress bar, then at least show
665          *something* to the user.  */
666       int ind = bp->tick % (progress_size * 2 - 6);
667       int i, pos;
668
669       /* Make the star move in two directions. */
670       if (ind < progress_size - 2)
671         pos = ind + 1;
672       else
673         pos = progress_size - (ind - progress_size + 5);
674
675       *p++ = '[';
676       for (i = 0; i < progress_size; i++)
677         {
678           if      (i == pos - 1) *p++ = '<';
679           else if (i == pos    ) *p++ = '=';
680           else if (i == pos + 1) *p++ = '>';
681           else
682             *p++ = ' ';
683         }
684       *p++ = ']';
685
686       ++bp->tick;
687     }
688
689   /* " 234,567,890" */
690   sprintf (p, " %-11s", legible (size));
691   p += strlen (p);
692
693   /* " 1012.45K/s" */
694   if (hist->summed_times && hist->summed_bytes)
695     {
696       static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
697       int units = 0;
698       double dlrate;
699       dlrate = calc_rate (hist->summed_bytes, hist->summed_times, &units);
700       sprintf (p, " %7.2f%s", dlrate, short_units[units]);
701       p += strlen (p);
702     }
703   else
704     APPEND_LITERAL ("   --.--K/s");
705
706   /* " ETA xx:xx:xx" */
707   if (bp->total_length > 0 && dl_total_time > 3000)
708     {
709       long eta;
710       int eta_hrs, eta_min, eta_sec;
711
712       /* Don't change the value of ETA more than approximately once
713          per second; doing so would cause flashing without providing
714          any value to the user. */
715       if (dl_total_time - bp->last_eta_time < 900
716           && bp->last_eta_value != 0)
717         eta = bp->last_eta_value;
718       else
719         {
720           /* Calculate ETA using the average download speed to predict
721              the future speed.  If you want to use the current speed
722              instead, replace dl_total_time with hist->summed_times
723              and bp->count with hist->summed_bytes.  I found that
724              doing that results in a very jerky and ultimately
725              unreliable ETA.  */
726           double time_sofar = (double)dl_total_time / 1000;
727           long bytes_remaining = bp->total_length - size;
728           eta = (long) (time_sofar * bytes_remaining / bp->count);
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       if (eta_hrs > 99)
738         goto no_eta;
739
740       if (eta_hrs == 0)
741         {
742           /* Hours not printed: pad with three spaces. */
743           APPEND_LITERAL ("   ");
744           sprintf (p, " ETA %02d:%02d", eta_min, eta_sec);
745         }
746       else
747         {
748           if (eta_hrs < 10)
749             /* Hours printed with one digit: pad with one space. */
750             *p++ = ' ';
751           sprintf (p, " ETA %d:%02d:%02d", eta_hrs, eta_min, eta_sec);
752         }
753       p += strlen (p);
754     }
755   else if (bp->total_length > 0)
756     {
757     no_eta:
758       APPEND_LITERAL ("             ");
759     }
760
761   assert (p - bp->buffer <= bp->width);
762
763   while (p < bp->buffer + bp->width)
764     *p++ = ' ';
765   *p = '\0';
766 }
767
768 /* Print the contents of the buffer as a one-line ASCII "image" so
769    that it can be overwritten next time.  */
770
771 static void
772 display_image (char *buf)
773 {
774   int old = log_set_save_context (0);
775   logputs (LOG_VERBOSE, "\r");
776   logputs (LOG_VERBOSE, buf);
777   log_set_save_context (old);
778 }
779
780 static void
781 bar_set_params (const char *params)
782 {
783   int sw;
784
785   if (params
786       && 0 == strcmp (params, "force"))
787     current_impl_locked = 1;
788
789   if ((opt.lfilename
790 #ifdef HAVE_ISATTY
791        || !isatty (fileno (stderr))
792 #else
793        1
794 #endif
795        )
796       && !current_impl_locked)
797     {
798       /* We're not printing to a TTY, so revert to the fallback
799          display.  #### We're recursively calling
800          set_progress_implementation here, which is slightly kludgy.
801          It would be nicer if we provided that function a return value
802          indicating a failure of some sort.  */
803       set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
804       return;
805     }
806
807   sw = determine_screen_width ();
808   if (sw && sw >= MINIMUM_SCREEN_WIDTH)
809     screen_width = sw;
810 }
811
812 #ifdef SIGWINCH
813 RETSIGTYPE
814 progress_handle_sigwinch (int sig)
815 {
816   int sw = determine_screen_width ();
817   if (sw && sw >= MINIMUM_SCREEN_WIDTH)
818     screen_width = sw;
819   signal (SIGWINCH, progress_handle_sigwinch);
820 }
821 #endif