]> sjero.net Git - wget/blob - src/progress.c
[svn] When the download is finished, print the time the download took.
[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 #include <string.h>
35 #include <assert.h>
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif
39 #include <signal.h>
40
41 #include "wget.h"
42 #include "progress.h"
43 #include "utils.h"
44 #include "retr.h"
45
46 struct progress_implementation {
47   const char *name;
48   bool interactive;
49   void *(*create) (wgint, wgint);
50   void (*update) (void *, wgint, double);
51   void (*finish) (void *, double);
52   void (*set_params) (const char *);
53 };
54
55 /* Necessary forward declarations. */
56
57 static void *dot_create (wgint, wgint);
58 static void dot_update (void *, wgint, double);
59 static void dot_finish (void *, double);
60 static void dot_set_params (const char *);
61
62 static void *bar_create (wgint, wgint);
63 static void bar_update (void *, wgint, double);
64 static void bar_finish (void *, double);
65 static void bar_set_params (const char *);
66
67 static struct progress_implementation implementations[] = {
68   { "dot", 0, dot_create, dot_update, dot_finish, dot_set_params },
69   { "bar", 1, bar_create, bar_update, bar_finish, bar_set_params }
70 };
71 static struct progress_implementation *current_impl;
72 static int current_impl_locked;
73
74 /* Progress implementation used by default.  Can be overriden in
75    wgetrc or by the fallback one.  */
76
77 #define DEFAULT_PROGRESS_IMPLEMENTATION "bar"
78
79 /* Fallnback progress implementation should be something that works
80    under all display types.  If you put something other than "dot"
81    here, remember that bar_set_params tries to switch to this if we're
82    not running on a TTY.  So changing this to "bar" could cause
83    infloop.  */
84
85 #define FALLBACK_PROGRESS_IMPLEMENTATION "dot"
86
87 /* Return true if NAME names a valid progress bar implementation.  The
88    characters after the first : will be ignored.  */
89
90 bool
91 valid_progress_implementation_p (const char *name)
92 {
93   int i;
94   struct progress_implementation *pi = implementations;
95   char *colon = strchr (name, ':');
96   int namelen = colon ? colon - name : strlen (name);
97
98   for (i = 0; i < countof (implementations); i++, pi++)
99     if (!strncmp (pi->name, name, namelen))
100       return true;
101   return false;
102 }
103
104 /* Set the progress implementation to NAME.  */
105
106 void
107 set_progress_implementation (const char *name)
108 {
109   int i, namelen;
110   struct progress_implementation *pi = implementations;
111   char *colon;
112
113   if (!name)
114     name = DEFAULT_PROGRESS_IMPLEMENTATION;
115
116   colon = strchr (name, ':');
117   namelen = colon ? colon - name : strlen (name);
118
119   for (i = 0; i < countof (implementations); i++, pi++)
120     if (!strncmp (pi->name, name, namelen))
121       {
122         current_impl = pi;
123         current_impl_locked = 0;
124
125         if (colon)
126           /* We call pi->set_params even if colon is NULL because we
127              want to give the implementation a chance to set up some
128              things it needs to run.  */
129           ++colon;
130
131         if (pi->set_params)
132           pi->set_params (colon);
133         return;
134       }
135   abort ();
136 }
137
138 static int output_redirected;
139
140 void
141 progress_schedule_redirect (void)
142 {
143   output_redirected = 1;
144 }
145
146 /* Create a progress gauge.  INITIAL is the number of bytes the
147    download starts from (zero if the download starts from scratch).
148    TOTAL is the expected total number of bytes in this download.  If
149    TOTAL is zero, it means that the download size is not known in
150    advance.  */
151
152 void *
153 progress_create (wgint initial, wgint total)
154 {
155   /* Check if the log status has changed under our feet. */
156   if (output_redirected)
157     {
158       if (!current_impl_locked)
159         set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
160       output_redirected = 0;
161     }
162
163   return current_impl->create (initial, total);
164 }
165
166 /* Return true if the progress gauge is "interactive", i.e. if it can
167    profit from being called regularly even in absence of data.  The
168    progress bar is interactive because it regularly updates the ETA
169    and current update.  */
170
171 bool
172 progress_interactive_p (void *progress)
173 {
174   return current_impl->interactive;
175 }
176
177 /* Inform the progress gauge of newly received bytes.  DLTIME is the
178    time in milliseconds since the beginning of the download.  */
179
180 void
181 progress_update (void *progress, wgint howmuch, double dltime)
182 {
183   current_impl->update (progress, howmuch, dltime);
184 }
185
186 /* Tell the progress gauge to clean up.  Calling this will free the
187    PROGRESS object, the further use of which is not allowed.  */
188
189 void
190 progress_finish (void *progress, double dltime)
191 {
192   current_impl->finish (progress, dltime);
193 }
194 \f
195 /* Dot-printing. */
196
197 struct dot_progress {
198   wgint initial_length;         /* how many bytes have been downloaded
199                                    previously. */
200   wgint total_length;           /* expected total byte count when the
201                                    download finishes */
202
203   int accumulated;
204
205   int rows;                     /* number of rows printed so far */
206   int dots;                     /* number of dots printed in this row */
207   double last_timer_value;
208 };
209
210 /* Dot-progress backend for progress_create. */
211
212 static void *
213 dot_create (wgint initial, wgint total)
214 {
215   struct dot_progress *dp = xnew0 (struct dot_progress);
216   dp->initial_length = initial;
217   dp->total_length   = total;
218
219   if (dp->initial_length)
220     {
221       int dot_bytes = opt.dot_bytes;
222       wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
223
224       int remainder = (int) (dp->initial_length % row_bytes);
225       wgint skipped = dp->initial_length - remainder;
226
227       if (skipped)
228         {
229           int skipped_k = (int) (skipped / 1024); /* skipped amount in K */
230           int skipped_k_len = numdigit (skipped_k);
231           if (skipped_k_len < 5)
232             skipped_k_len = 5;
233
234           /* Align the [ skipping ... ] line with the dots.  To do
235              that, insert the number of spaces equal to the number of
236              digits in the skipped amount in K.  */
237           logprintf (LOG_VERBOSE, _("\n%*s[ skipping %dK ]"),
238                      2 + skipped_k_len, "", skipped_k);
239         }
240
241       logprintf (LOG_VERBOSE, "\n%5ldK", (long) (skipped / 1024));
242       for (; remainder >= dot_bytes; remainder -= dot_bytes)
243         {
244           if (dp->dots % opt.dot_spacing == 0)
245             logputs (LOG_VERBOSE, " ");
246           logputs (LOG_VERBOSE, ",");
247           ++dp->dots;
248         }
249       assert (dp->dots < opt.dots_in_line);
250
251       dp->accumulated = remainder;
252       dp->rows = skipped / row_bytes;
253     }
254
255   return dp;
256 }
257
258 static void
259 print_percentage (wgint bytes, wgint expected)
260 {
261   int percentage = (int)(100.0 * bytes / expected);
262   logprintf (LOG_VERBOSE, "%3d%%", percentage);
263 }
264
265 static void
266 print_download_speed (struct dot_progress *dp, wgint bytes, double dltime)
267 {
268   logprintf (LOG_VERBOSE, " %7s",
269              retr_rate (bytes, dltime - dp->last_timer_value));
270   dp->last_timer_value = dltime;
271 }
272
273 /* Dot-progress backend for progress_update. */
274
275 static void
276 dot_update (void *progress, wgint howmuch, double dltime)
277 {
278   struct dot_progress *dp = progress;
279   int dot_bytes = opt.dot_bytes;
280   wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
281
282   log_set_flush (false);
283
284   dp->accumulated += howmuch;
285   for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
286     {
287       if (dp->dots == 0)
288         logprintf (LOG_VERBOSE, "\n%5ldK", (long) (dp->rows * row_bytes / 1024));
289
290       if (dp->dots % opt.dot_spacing == 0)
291         logputs (LOG_VERBOSE, " ");
292       logputs (LOG_VERBOSE, ".");
293
294       ++dp->dots;
295       if (dp->dots >= opt.dots_in_line)
296         {
297           wgint row_qty = row_bytes;
298           if (dp->rows == dp->initial_length / row_bytes)
299             row_qty -= dp->initial_length % row_bytes;
300
301           ++dp->rows;
302           dp->dots = 0;
303
304           if (dp->total_length)
305             print_percentage (dp->rows * row_bytes, dp->total_length);
306           print_download_speed (dp, row_qty, dltime);
307         }
308     }
309
310   log_set_flush (true);
311 }
312
313 /* Dot-progress backend for progress_finish. */
314
315 static void
316 dot_finish (void *progress, double dltime)
317 {
318   struct dot_progress *dp = progress;
319   int dot_bytes = opt.dot_bytes;
320   wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
321   int i;
322
323   log_set_flush (false);
324
325   if (dp->dots == 0)
326     logprintf (LOG_VERBOSE, "\n%5ldK", (long) (dp->rows * row_bytes / 1024));
327   for (i = dp->dots; i < opt.dots_in_line; i++)
328     {
329       if (i % opt.dot_spacing == 0)
330         logputs (LOG_VERBOSE, " ");
331       logputs (LOG_VERBOSE, " ");
332     }
333   if (dp->total_length)
334     {
335       print_percentage (dp->rows * row_bytes
336                         + dp->dots * dot_bytes
337                         + dp->accumulated,
338                         dp->total_length);
339     }
340
341   {
342     wgint row_qty = dp->dots * dot_bytes + dp->accumulated;
343     if (dp->rows == dp->initial_length / row_bytes)
344       row_qty -= dp->initial_length % row_bytes;
345     print_download_speed (dp, row_qty, dltime);
346   }
347
348   logputs (LOG_VERBOSE, "\n\n");
349   log_set_flush (false);
350
351   xfree (dp);
352 }
353
354 /* This function interprets the progress "parameters".  For example,
355    if Wget is invoked with --progress=dot:mega, it will set the
356    "dot-style" to "mega".  Valid styles are default, binary, mega, and
357    giga.  */
358
359 static void
360 dot_set_params (const char *params)
361 {
362   if (!params || !*params)
363     params = opt.dot_style;
364
365   if (!params)
366     return;
367
368   /* We use this to set the retrieval style.  */
369   if (!strcasecmp (params, "default"))
370     {
371       /* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
372          line.  */
373       opt.dot_bytes = 1024;
374       opt.dot_spacing = 10;
375       opt.dots_in_line = 50;
376     }
377   else if (!strcasecmp (params, "binary"))
378     {
379       /* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
380          (384K) in a line.  */
381       opt.dot_bytes = 8192;
382       opt.dot_spacing = 16;
383       opt.dots_in_line = 48;
384     }
385   else if (!strcasecmp (params, "mega"))
386     {
387       /* "Mega" retrieval, for retrieving very long files; each dot is
388          64K, 8 dots in a cluster, 6 clusters (3M) in a line.  */
389       opt.dot_bytes = 65536L;
390       opt.dot_spacing = 8;
391       opt.dots_in_line = 48;
392     }
393   else if (!strcasecmp (params, "giga"))
394     {
395       /* "Giga" retrieval, for retrieving very very *very* long files;
396          each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
397          line.  */
398       opt.dot_bytes = (1L << 20);
399       opt.dot_spacing = 8;
400       opt.dots_in_line = 32;
401     }
402   else
403     fprintf (stderr,
404              _("Invalid dot style specification `%s'; leaving unchanged.\n"),
405              params);
406 }
407 \f
408 /* "Thermometer" (bar) progress. */
409
410 /* Assumed screen width if we can't find the real value.  */
411 #define DEFAULT_SCREEN_WIDTH 80
412
413 /* Minimum screen width we'll try to work with.  If this is too small,
414    create_image will overflow the buffer.  */
415 #define MINIMUM_SCREEN_WIDTH 45
416
417 /* The last known screen width.  This can be updated by the code that
418    detects that SIGWINCH was received (but it's never updated from the
419    signal handler).  */
420 static int screen_width;
421
422 /* A flag that, when set, means SIGWINCH was received.  */
423 static volatile sig_atomic_t received_sigwinch;
424
425 /* Size of the download speed history ring. */
426 #define DLSPEED_HISTORY_SIZE 20
427
428 /* The minimum time length of a history sample.  By default, each
429    sample is at least 150ms long, which means that, over the course of
430    20 samples, "current" download speed spans at least 3s into the
431    past.  */
432 #define DLSPEED_SAMPLE_MIN 150
433
434 /* The time after which the download starts to be considered
435    "stalled", i.e. the current bandwidth is not printed and the recent
436    download speeds are scratched.  */
437 #define STALL_START_TIME 5000
438
439 struct bar_progress {
440   wgint initial_length;         /* how many bytes have been downloaded
441                                    previously. */
442   wgint total_length;           /* expected total byte count when the
443                                    download finishes */
444   wgint count;                  /* bytes downloaded so far */
445
446   double last_screen_update;    /* time of the last screen update,
447                                    measured since the beginning of
448                                    download. */
449
450   int width;                    /* screen width we're using at the
451                                    time the progress gauge was
452                                    created.  this is different from
453                                    the screen_width global variable in
454                                    that the latter can be changed by a
455                                    signal. */
456   char *buffer;                 /* buffer where the bar "image" is
457                                    stored. */
458   int tick;                     /* counter used for drawing the
459                                    progress bar where the total size
460                                    is not known. */
461
462   /* The following variables (kept in a struct for namespace reasons)
463      keep track of recent download speeds.  See bar_update() for
464      details.  */
465   struct bar_progress_hist {
466     int pos;
467     wgint times[DLSPEED_HISTORY_SIZE];
468     wgint bytes[DLSPEED_HISTORY_SIZE];
469
470     /* The sum of times and bytes respectively, maintained for
471        efficiency. */
472     wgint total_time;
473     wgint total_bytes;
474   } hist;
475
476   double recent_start;          /* timestamp of beginning of current
477                                    position. */
478   wgint recent_bytes;           /* bytes downloaded so far. */
479
480   bool stalled;                 /* set when no data arrives for longer
481                                    than STALL_START_TIME, then reset
482                                    when new data arrives. */
483
484   /* create_image() uses these to make sure that ETA information
485      doesn't flicker. */
486   double last_eta_time;         /* time of the last update to download
487                                    speed and ETA, measured since the
488                                    beginning of download. */
489   int last_eta_value;
490 };
491
492 static void create_image (struct bar_progress *, double, bool);
493 static void display_image (char *);
494
495 static void *
496 bar_create (wgint initial, wgint total)
497 {
498   struct bar_progress *bp = xnew0 (struct bar_progress);
499
500   /* In theory, our callers should take care of this pathological
501      case, but it can sometimes happen. */
502   if (initial > total)
503     total = initial;
504
505   bp->initial_length = initial;
506   bp->total_length   = total;
507
508   /* Initialize screen_width if this hasn't been done or if it might
509      have changed, as indicated by receiving SIGWINCH.  */
510   if (!screen_width || received_sigwinch)
511     {
512       screen_width = determine_screen_width ();
513       if (!screen_width)
514         screen_width = DEFAULT_SCREEN_WIDTH;
515       else if (screen_width < MINIMUM_SCREEN_WIDTH)
516         screen_width = MINIMUM_SCREEN_WIDTH;
517       received_sigwinch = 0;
518     }
519
520   /* - 1 because we don't want to use the last screen column. */
521   bp->width = screen_width - 1;
522   /* + 1 for the terminating zero. */
523   bp->buffer = xmalloc (bp->width + 1);
524
525   logputs (LOG_VERBOSE, "\n");
526
527   create_image (bp, 0, false);
528   display_image (bp->buffer);
529
530   return bp;
531 }
532
533 static void update_speed_ring (struct bar_progress *, wgint, double);
534
535 static void
536 bar_update (void *progress, wgint howmuch, double dltime)
537 {
538   struct bar_progress *bp = progress;
539   bool force_screen_update = false;
540
541   bp->count += howmuch;
542   if (bp->total_length > 0
543       && bp->count + bp->initial_length > bp->total_length)
544     /* We could be downloading more than total_length, e.g. when the
545        server sends an incorrect Content-Length header.  In that case,
546        adjust bp->total_length to the new reality, so that the code in
547        create_image() that depends on total size being smaller or
548        equal to the expected size doesn't abort.  */
549     bp->total_length = bp->initial_length + bp->count;
550
551   update_speed_ring (bp, howmuch, dltime);
552
553   /* If SIGWINCH (the window size change signal) been received,
554      determine the new screen size and update the screen.  */
555   if (received_sigwinch)
556     {
557       int old_width = screen_width;
558       screen_width = determine_screen_width ();
559       if (!screen_width)
560         screen_width = DEFAULT_SCREEN_WIDTH;
561       else if (screen_width < MINIMUM_SCREEN_WIDTH)
562         screen_width = MINIMUM_SCREEN_WIDTH;
563       if (screen_width != old_width)
564         {
565           bp->width = screen_width - 1;
566           bp->buffer = xrealloc (bp->buffer, bp->width + 1);
567           force_screen_update = true;
568         }
569       received_sigwinch = 0;
570     }
571
572   if (dltime - bp->last_screen_update < 200 && !force_screen_update)
573     /* Don't update more often than five times per second. */
574     return;
575
576   create_image (bp, dltime, false);
577   display_image (bp->buffer);
578   bp->last_screen_update = dltime;
579 }
580
581 static void
582 bar_finish (void *progress, double dltime)
583 {
584   struct bar_progress *bp = progress;
585
586   if (bp->total_length > 0
587       && bp->count + bp->initial_length > bp->total_length)
588     /* See bar_update() for explanation. */
589     bp->total_length = bp->initial_length + bp->count;
590
591   create_image (bp, dltime, true);
592   display_image (bp->buffer);
593
594   logputs (LOG_VERBOSE, "\n\n");
595
596   xfree (bp->buffer);
597   xfree (bp);
598 }
599
600 /* This code attempts to maintain the notion of a "current" download
601    speed, over the course of no less than 3s.  (Shorter intervals
602    produce very erratic results.)
603
604    To do so, it samples the speed in 150ms intervals and stores the
605    recorded samples in a FIFO history ring.  The ring stores no more
606    than 20 intervals, hence the history covers the period of at least
607    three seconds and at most 20 reads into the past.  This method
608    should produce reasonable results for downloads ranging from very
609    slow to very fast.
610
611    The idea is that for fast downloads, we get the speed over exactly
612    the last three seconds.  For slow downloads (where a network read
613    takes more than 150ms to complete), we get the speed over a larger
614    time period, as large as it takes to complete thirty reads.  This
615    is good because slow downloads tend to fluctuate more and a
616    3-second average would be too erratic.  */
617
618 static void
619 update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime)
620 {
621   struct bar_progress_hist *hist = &bp->hist;
622   double recent_age = dltime - bp->recent_start;
623
624   /* Update the download count. */
625   bp->recent_bytes += howmuch;
626
627   /* For very small time intervals, we return after having updated the
628      "recent" download count.  When its age reaches or exceeds minimum
629      sample time, it will be recorded in the history ring.  */
630   if (recent_age < DLSPEED_SAMPLE_MIN)
631     return;
632
633   if (howmuch == 0)
634     {
635       /* If we're not downloading anything, we might be stalling,
636          i.e. not downloading anything for an extended period of time.
637          Since 0-reads do not enter the history ring, recent_age
638          effectively measures the time since last read.  */
639       if (recent_age >= STALL_START_TIME)
640         {
641           /* If we're stalling, reset the ring contents because it's
642              stale and because it will make bar_update stop printing
643              the (bogus) current bandwidth.  */
644           bp->stalled = true;
645           xzero (*hist);
646           bp->recent_bytes = 0;
647         }
648       return;
649     }
650
651   /* We now have a non-zero amount of to store to the speed ring.  */
652
653   /* If the stall status was acquired, reset it. */
654   if (bp->stalled)
655     {
656       bp->stalled = false;
657       /* "recent_age" includes the the entired stalled period, which
658          could be very long.  Don't update the speed ring with that
659          value because the current bandwidth would start too small.
660          Start with an arbitrary (but more reasonable) time value and
661          let it level out.  */
662       recent_age = 1000;
663     }
664
665   /* Store "recent" bytes and download time to history ring at the
666      position POS.  */
667
668   /* To correctly maintain the totals, first invalidate existing data
669      (least recent in time) at this position. */
670   hist->total_time  -= hist->times[hist->pos];
671   hist->total_bytes -= hist->bytes[hist->pos];
672
673   /* Now store the new data and update the totals. */
674   hist->times[hist->pos] = recent_age;
675   hist->bytes[hist->pos] = bp->recent_bytes;
676   hist->total_time  += recent_age;
677   hist->total_bytes += bp->recent_bytes;
678
679   /* Start a new "recent" period. */
680   bp->recent_start = dltime;
681   bp->recent_bytes = 0;
682
683   /* Advance the current ring position. */
684   if (++hist->pos == DLSPEED_HISTORY_SIZE)
685     hist->pos = 0;
686
687 #if 0
688   /* Sledgehammer check to verify that the totals are accurate. */
689   {
690     int i;
691     double sumt = 0, sumb = 0;
692     for (i = 0; i < DLSPEED_HISTORY_SIZE; i++)
693       {
694         sumt += hist->times[i];
695         sumb += hist->bytes[i];
696       }
697     assert (sumt == hist->total_time);
698     assert (sumb == hist->total_bytes);
699   }
700 #endif
701 }
702
703 static const char *eta_to_human_short (int);
704
705 #define APPEND_LITERAL(s) do {                  \
706   memcpy (p, s, sizeof (s) - 1);                \
707   p += sizeof (s) - 1;                          \
708 } while (0)
709
710 #ifndef MAX
711 # define MAX(a, b) ((a) >= (b) ? (a) : (b))
712 #endif
713
714 static void
715 create_image (struct bar_progress *bp, double dl_total_time, bool done)
716 {
717   char *p = bp->buffer;
718   wgint size = bp->initial_length + bp->count;
719
720   const char *size_grouped = with_thousand_seps (size);
721   int size_grouped_len = strlen (size_grouped);
722
723   struct bar_progress_hist *hist = &bp->hist;
724
725   /* The progress bar should look like this:
726      xx% [=======>             ] nn,nnn 12.34K/s  eta 36m 51s
727
728      Calculate the geometry.  The idea is to assign as much room as
729      possible to the progress bar.  The other idea is to never let
730      things "jitter", i.e. pad elements that vary in size so that
731      their variance does not affect the placement of other elements.
732      It would be especially bad for the progress bar to be resized
733      randomly.
734
735      "xx% " or "100%"  - percentage               - 4 chars
736      "[]"              - progress bar decorations - 2 chars
737      " nnn,nnn,nnn"    - downloaded bytes         - 12 chars or very rarely more
738      " 1012.56K/s"     - dl rate                  - 11 chars
739      "  eta 36m 51s"   - ETA                      - 13 chars
740
741      "=====>..."       - progress bar             - the rest
742   */
743   int dlbytes_size = 1 + MAX (size_grouped_len, 11);
744   int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);
745
746   if (progress_size < 5)
747     progress_size = 0;
748
749   /* "xx% " */
750   if (bp->total_length > 0)
751     {
752       int percentage = (int)(100.0 * size / bp->total_length);
753       assert (percentage <= 100);
754
755       if (percentage < 100)
756         sprintf (p, "%2d%% ", percentage);
757       else
758         strcpy (p, "100%");
759       p += 4;
760     }
761   else
762     APPEND_LITERAL ("    ");
763
764   /* The progress bar: "[====>      ]" or "[++==>      ]". */
765   if (progress_size && bp->total_length > 0)
766     {
767       /* Size of the initial portion. */
768       int insz = (double)bp->initial_length / bp->total_length * progress_size;
769
770       /* Size of the downloaded portion. */
771       int dlsz = (double)size / bp->total_length * progress_size;
772
773       char *begin;
774       int i;
775
776       assert (dlsz <= progress_size);
777       assert (insz <= dlsz);
778
779       *p++ = '[';
780       begin = p;
781
782       /* Print the initial portion of the download with '+' chars, the
783          rest with '=' and one '>'.  */
784       for (i = 0; i < insz; i++)
785         *p++ = '+';
786       dlsz -= insz;
787       if (dlsz > 0)
788         {
789           for (i = 0; i < dlsz - 1; i++)
790             *p++ = '=';
791           *p++ = '>';
792         }
793
794       while (p - begin < progress_size)
795         *p++ = ' ';
796       *p++ = ']';
797     }
798   else if (progress_size)
799     {
800       /* If we can't draw a real progress bar, then at least show
801          *something* to the user.  */
802       int ind = bp->tick % (progress_size * 2 - 6);
803       int i, pos;
804
805       /* Make the star move in two directions. */
806       if (ind < progress_size - 2)
807         pos = ind + 1;
808       else
809         pos = progress_size - (ind - progress_size + 5);
810
811       *p++ = '[';
812       for (i = 0; i < progress_size; i++)
813         {
814           if      (i == pos - 1) *p++ = '<';
815           else if (i == pos    ) *p++ = '=';
816           else if (i == pos + 1) *p++ = '>';
817           else
818             *p++ = ' ';
819         }
820       *p++ = ']';
821
822       ++bp->tick;
823     }
824
825   /* " 234,567,890" */
826   sprintf (p, " %-11s", size_grouped);
827   p += strlen (p);
828
829   /* " 1012.45K/s" */
830   if (hist->total_time && hist->total_bytes)
831     {
832       static const char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
833       int units = 0;
834       /* Calculate the download speed using the history ring and
835          recent data that hasn't made it to the ring yet.  */
836       wgint dlquant = hist->total_bytes + bp->recent_bytes;
837       double dltime = hist->total_time + (dl_total_time - bp->recent_start);
838       double dlspeed = calc_rate (dlquant, dltime, &units);
839       sprintf (p, " %7.2f%s", dlspeed, short_units[units]);
840       p += strlen (p);
841     }
842   else
843     APPEND_LITERAL ("   --.--K/s");
844
845   if (!done)
846     {
847       /* "  eta ..m ..s"; wait for three seconds before displaying the ETA.
848          That's because the ETA value needs a while to become
849          reliable.  */
850       if (bp->total_length > 0 && bp->count > 0 && dl_total_time > 3000)
851         {
852           int eta;
853
854           /* Don't change the value of ETA more than approximately once
855              per second; doing so would cause flashing without providing
856              any value to the user. */
857           if (bp->total_length != size
858               && bp->last_eta_value != 0
859               && dl_total_time - bp->last_eta_time < 900)
860             eta = bp->last_eta_value;
861           else
862             {
863               /* Calculate ETA using the average download speed to predict
864                  the future speed.  If you want to use a speed averaged
865                  over a more recent period, replace dl_total_time with
866                  hist->total_time and bp->count with hist->total_bytes.
867                  I found that doing that results in a very jerky and
868                  ultimately unreliable ETA.  */
869               double time_sofar = (double) dl_total_time / 1000;
870               wgint bytes_remaining = bp->total_length - size;
871               eta = (int) (time_sofar * bytes_remaining / bp->count + 0.5);
872               bp->last_eta_value = eta;
873               bp->last_eta_time = dl_total_time;
874             }
875
876           sprintf (p, "  eta %s", eta_to_human_short (eta));
877           p += strlen (p);
878         }
879       else if (bp->total_length > 0)
880         {
881           APPEND_LITERAL ("             ");
882         }
883     }
884   else
885     {
886       /* When the download is done, print the elapsed time.  */
887       sprintf (p, _("   in %s"), eta_to_human_short (dl_total_time / 1000 + 0.5));
888       p += strlen (p);
889     }
890
891   assert (p - bp->buffer <= bp->width);
892
893   while (p < bp->buffer + bp->width)
894     *p++ = ' ';
895   *p = '\0';
896 }
897
898 /* Print the contents of the buffer as a one-line ASCII "image" so
899    that it can be overwritten next time.  */
900
901 static void
902 display_image (char *buf)
903 {
904   bool old = log_set_save_context (false);
905   logputs (LOG_VERBOSE, "\r");
906   logputs (LOG_VERBOSE, buf);
907   log_set_save_context (old);
908 }
909
910 static void
911 bar_set_params (const char *params)
912 {
913   char *term = getenv ("TERM");
914
915   if (params
916       && 0 == strcmp (params, "force"))
917     current_impl_locked = 1;
918
919   if ((opt.lfilename
920 #ifdef HAVE_ISATTY
921        /* The progress bar doesn't make sense if the output is not a
922           TTY -- when logging to file, it is better to review the
923           dots.  */
924        || !isatty (fileno (stderr))
925 #endif
926        /* Normally we don't depend on terminal type because the
927           progress bar only uses ^M to move the cursor to the
928           beginning of line, which works even on dumb terminals.  But
929           Jamie Zawinski reports that ^M and ^H tricks don't work in
930           Emacs shell buffers, and only make a mess.  */
931        || (term && 0 == strcmp (term, "emacs"))
932        )
933       && !current_impl_locked)
934     {
935       /* We're not printing to a TTY, so revert to the fallback
936          display.  #### We're recursively calling
937          set_progress_implementation here, which is slightly kludgy.
938          It would be nicer if we provided that function a return value
939          indicating a failure of some sort.  */
940       set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
941       return;
942     }
943 }
944
945 #ifdef SIGWINCH
946 void
947 progress_handle_sigwinch (int sig)
948 {
949   received_sigwinch = 1;
950   signal (SIGWINCH, progress_handle_sigwinch);
951 }
952 #endif
953
954 /* Provide a short human-readable rendition of the ETA.  It never
955    occupies more than 7 characters of screen space.  */
956
957 static const char *
958 eta_to_human_short (int secs)
959 {
960   static char buf[10];          /* 8 is enough, but just in case */
961   static int last = -1;
962
963   /* Trivial optimization.  This function can be called every 200
964      msecs (see bar_update) for fast downloads, but ETA will only
965      change once per 900 msecs (see create_image).  */
966   if (secs == last)
967     return buf;
968   last = secs;
969
970   if (secs < 100)
971     sprintf (buf, _("%ds"), secs);
972   else if (secs < 100 * 60)
973     sprintf (buf, _("%dm %ds"), secs / 60, secs % 60);
974   else if (secs < 100 * 3600)
975     sprintf (buf, _("%dh %dm"), secs / 3600, (secs / 60) % 60);
976   else if (secs < 100 * 86400)
977     sprintf (buf, _("%dd %dh"), secs / 86400, (secs / 3600) % 60);
978   else
979     /* (2^31-1)/86400 doesn't overflow BUF. */
980     sprintf (buf, _("%dd"), secs / 86400);
981
982   return buf;
983 }