]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/ankh/examples/wget/contrib/src/progress.c
Inital import
[l4.git] / l4 / pkg / ankh / examples / wget / contrib / src / progress.c
1 /* Download progress.
2    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Wget.
6
7 GNU Wget is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 GNU Wget is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Wget.  If not, see <http://www.gnu.org/licenses/>.
19
20 Additional permission under GNU GPL version 3 section 7
21
22 If you modify this program, or any covered work, by linking or
23 combining it with the OpenSSL project's OpenSSL library (or a
24 modified version of that library), containing parts covered by the
25 terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
26 grants you additional permission to convey the resulting work.
27 Corresponding Source for a non-source form of such a combination
28 shall include the source code for the parts of OpenSSL used as well
29 as that of the covered work.  */
30
31 #include <config.h>
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <assert.h>
37 #ifdef HAVE_UNISTD_H
38 # include <unistd.h>
39 #endif
40 #include <signal.h>
41 #ifdef HAVE_WCHAR_H
42 # include <wchar.h>
43 #endif
44
45 #include "wget.h"
46 #include "progress.h"
47 #include "utils.h"
48 #include "retr.h"
49
50 struct progress_implementation {
51   const char *name;
52   bool interactive;
53   void *(*create) (wgint, wgint);
54   void (*update) (void *, wgint, double);
55   void (*finish) (void *, double);
56   void (*set_params) (const char *);
57 };
58
59 /* Necessary forward declarations. */
60
61 static void *dot_create (wgint, wgint);
62 static void dot_update (void *, wgint, double);
63 static void dot_finish (void *, double);
64 static void dot_set_params (const char *);
65
66 static void *bar_create (wgint, wgint);
67 static void bar_update (void *, wgint, double);
68 static void bar_finish (void *, double);
69 static void bar_set_params (const char *);
70
71 static struct progress_implementation implementations[] = {
72   { "dot", 0, dot_create, dot_update, dot_finish, dot_set_params },
73   { "bar", 1, bar_create, bar_update, bar_finish, bar_set_params }
74 };
75 static struct progress_implementation *current_impl;
76 static int current_impl_locked;
77
78 /* Progress implementation used by default.  Can be overriden in
79    wgetrc or by the fallback one.  */
80
81 #define DEFAULT_PROGRESS_IMPLEMENTATION "bar"
82
83 /* Fallback progress implementation should be something that works
84    under all display types.  If you put something other than "dot"
85    here, remember that bar_set_params tries to switch to this if we're
86    not running on a TTY.  So changing this to "bar" could cause
87    infloop.  */
88
89 #define FALLBACK_PROGRESS_IMPLEMENTATION "dot"
90
91 /* Return true if NAME names a valid progress bar implementation.  The
92    characters after the first : will be ignored.  */
93
94 bool
95 valid_progress_implementation_p (const char *name)
96 {
97   int i;
98   struct progress_implementation *pi = implementations;
99   char *colon = strchr (name, ':');
100   int namelen = colon ? colon - name : strlen (name);
101
102   for (i = 0; i < countof (implementations); i++, pi++)
103     if (!strncmp (pi->name, name, namelen))
104       return true;
105   return false;
106 }
107
108 /* Set the progress implementation to NAME.  */
109
110 void
111 set_progress_implementation (const char *name)
112 {
113   int i, namelen;
114   struct progress_implementation *pi = implementations;
115   const char *colon;
116
117   if (!name)
118     name = DEFAULT_PROGRESS_IMPLEMENTATION;
119
120   colon = strchr (name, ':');
121   namelen = colon ? colon - name : strlen (name);
122
123   for (i = 0; i < countof (implementations); i++, pi++)
124     if (!strncmp (pi->name, name, namelen))
125       {
126         current_impl = pi;
127         current_impl_locked = 0;
128
129         if (colon)
130           /* We call pi->set_params even if colon is NULL because we
131              want to give the implementation a chance to set up some
132              things it needs to run.  */
133           ++colon;
134
135         if (pi->set_params)
136           pi->set_params (colon);
137         return;
138       }
139   abort ();
140 }
141
142 static int output_redirected;
143
144 void
145 progress_schedule_redirect (void)
146 {
147   output_redirected = 1;
148 }
149
150 /* Create a progress gauge.  INITIAL is the number of bytes the
151    download starts from (zero if the download starts from scratch).
152    TOTAL is the expected total number of bytes in this download.  If
153    TOTAL is zero, it means that the download size is not known in
154    advance.  */
155
156 void *
157 progress_create (wgint initial, wgint total)
158 {
159   /* Check if the log status has changed under our feet. */
160   if (output_redirected)
161     {
162       if (!current_impl_locked)
163         set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
164       output_redirected = 0;
165     }
166
167   return current_impl->create (initial, total);
168 }
169
170 /* Return true if the progress gauge is "interactive", i.e. if it can
171    profit from being called regularly even in absence of data.  The
172    progress bar is interactive because it regularly updates the ETA
173    and current update.  */
174
175 bool
176 progress_interactive_p (void *progress)
177 {
178   return current_impl->interactive;
179 }
180
181 /* Inform the progress gauge of newly received bytes.  DLTIME is the
182    time since the beginning of the download.  */
183
184 void
185 progress_update (void *progress, wgint howmuch, double dltime)
186 {
187   current_impl->update (progress, howmuch, dltime);
188 }
189
190 /* Tell the progress gauge to clean up.  Calling this will free the
191    PROGRESS object, the further use of which is not allowed.  */
192
193 void
194 progress_finish (void *progress, double dltime)
195 {
196   current_impl->finish (progress, dltime);
197 }
198 \f
199 /* Dot-printing. */
200
201 struct dot_progress {
202   wgint initial_length;         /* how many bytes have been downloaded
203                                    previously. */
204   wgint total_length;           /* expected total byte count when the
205                                    download finishes */
206
207   int accumulated;              /* number of bytes accumulated after
208                                    the last printed dot */
209
210   int rows;                     /* number of rows printed so far */
211   int dots;                     /* number of dots printed in this row */
212
213   double last_timer_value;
214 };
215
216 /* Dot-progress backend for progress_create. */
217
218 static void *
219 dot_create (wgint initial, wgint total)
220 {
221   struct dot_progress *dp = xnew0 (struct dot_progress);
222   dp->initial_length = initial;
223   dp->total_length   = total;
224
225   if (dp->initial_length)
226     {
227       int dot_bytes = opt.dot_bytes;
228       const wgint ROW_BYTES = opt.dot_bytes * opt.dots_in_line;
229
230       int remainder = dp->initial_length % ROW_BYTES;
231       wgint skipped = dp->initial_length - remainder;
232
233       if (skipped)
234         {
235           wgint skipped_k = skipped / 1024; /* skipped amount in K */
236           int skipped_k_len = numdigit (skipped_k);
237           if (skipped_k_len < 6)
238             skipped_k_len = 6;
239
240           /* Align the [ skipping ... ] line with the dots.  To do
241              that, insert the number of spaces equal to the number of
242              digits in the skipped amount in K.  */
243           logprintf (LOG_VERBOSE, _("\n%*s[ skipping %sK ]"),
244                      2 + skipped_k_len, "",
245                      number_to_static_string (skipped_k));
246         }
247
248       logprintf (LOG_VERBOSE, "\n%6sK",
249                  number_to_static_string (skipped / 1024));
250       for (; remainder >= dot_bytes; remainder -= dot_bytes)
251         {
252           if (dp->dots % opt.dot_spacing == 0)
253             logputs (LOG_VERBOSE, " ");
254           logputs (LOG_VERBOSE, ",");
255           ++dp->dots;
256         }
257       assert (dp->dots < opt.dots_in_line);
258
259       dp->accumulated = remainder;
260       dp->rows = skipped / ROW_BYTES;
261     }
262
263   return dp;
264 }
265
266 static const char *eta_to_human_short (int, bool);
267
268 /* Prints the stats (percentage of completion, speed, ETA) for current
269    row.  DLTIME is the time spent downloading the data in current
270    row.
271
272    #### This function is somewhat uglified by the fact that current
273    row and last row have somewhat different stats requirements.  It
274    might be worthwhile to split it to two different functions.  */
275
276 static void
277 print_row_stats (struct dot_progress *dp, double dltime, bool last)
278 {
279   const wgint ROW_BYTES = opt.dot_bytes * opt.dots_in_line;
280
281   /* bytes_displayed is the number of bytes indicated to the user by
282      dots printed so far, includes the initially "skipped" amount */
283   wgint bytes_displayed = dp->rows * ROW_BYTES + dp->dots * opt.dot_bytes;
284
285   if (last)
286     /* For last row also count bytes accumulated after last dot */
287     bytes_displayed += dp->accumulated;
288
289   if (dp->total_length)
290     {
291       /* Round to floor value to provide gauge how much data *has*
292          been retrieved.  12.8% will round to 12% because the 13% mark
293          has not yet been reached.  100% is only shown when done.  */
294       int percentage = 100.0 * bytes_displayed / dp->total_length;
295       logprintf (LOG_VERBOSE, "%3d%%", percentage);
296     }
297
298   {
299     static char names[] = {' ', 'K', 'M', 'G'};
300     int units;
301     double rate;
302     wgint bytes_this_row;
303     if (!last)
304       bytes_this_row = ROW_BYTES;
305     else
306       /* For last row also include bytes accumulated after last dot.  */
307       bytes_this_row = dp->dots * opt.dot_bytes + dp->accumulated;
308     /* Don't count the portion of the row belonging to initial_length */
309     if (dp->rows == dp->initial_length / ROW_BYTES)
310       bytes_this_row -= dp->initial_length % ROW_BYTES;
311     rate = calc_rate (bytes_this_row, dltime - dp->last_timer_value, &units);
312     logprintf (LOG_VERBOSE, " %4.*f%c",
313                rate >= 99.95 ? 0 : rate >= 9.995 ? 1 : 2,
314                rate, names[units]);
315     dp->last_timer_value = dltime;
316   }
317
318   if (!last)
319     {
320       /* Display ETA based on average speed.  Inspired by Vladi
321          Belperchinov-Shabanski's "wget-new-percentage" patch.  */
322       if (dp->total_length)
323         {
324           wgint bytes_remaining = dp->total_length - bytes_displayed;
325           /* The quantity downloaded in this download run. */
326           wgint bytes_sofar = bytes_displayed - dp->initial_length;
327           double eta = dltime * bytes_remaining / bytes_sofar;
328           if (eta < INT_MAX - 1)
329             logprintf (LOG_VERBOSE, " %s",
330                        eta_to_human_short ((int) (eta + 0.5), true));
331         }
332     }
333   else
334     {
335       /* When done, print the total download time */
336       if (dltime >= 10)
337         logprintf (LOG_VERBOSE, "=%s",
338                    eta_to_human_short ((int) (dltime + 0.5), true));
339       else
340         logprintf (LOG_VERBOSE, "=%ss", print_decimal (dltime));
341     }
342 }
343
344 /* Dot-progress backend for progress_update. */
345
346 static void
347 dot_update (void *progress, wgint howmuch, double dltime)
348 {
349   struct dot_progress *dp = progress;
350   int dot_bytes = opt.dot_bytes;
351   wgint ROW_BYTES = opt.dot_bytes * opt.dots_in_line;
352
353   log_set_flush (false);
354
355   dp->accumulated += howmuch;
356   for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
357     {
358       if (dp->dots == 0)
359         logprintf (LOG_VERBOSE, "\n%6sK",
360                    number_to_static_string (dp->rows * ROW_BYTES / 1024));
361
362       if (dp->dots % opt.dot_spacing == 0)
363         logputs (LOG_VERBOSE, " ");
364       logputs (LOG_VERBOSE, ".");
365
366       ++dp->dots;
367       if (dp->dots >= opt.dots_in_line)
368         {
369           ++dp->rows;
370           dp->dots = 0;
371
372           print_row_stats (dp, dltime, false);
373         }
374     }
375
376   log_set_flush (true);
377 }
378
379 /* Dot-progress backend for progress_finish. */
380
381 static void
382 dot_finish (void *progress, double dltime)
383 {
384   struct dot_progress *dp = progress;
385   wgint ROW_BYTES = opt.dot_bytes * opt.dots_in_line;
386   int i;
387
388   log_set_flush (false);
389
390   if (dp->dots == 0)
391     logprintf (LOG_VERBOSE, "\n%6sK",
392                number_to_static_string (dp->rows * ROW_BYTES / 1024));
393   for (i = dp->dots; i < opt.dots_in_line; i++)
394     {
395       if (i % opt.dot_spacing == 0)
396         logputs (LOG_VERBOSE, " ");
397       logputs (LOG_VERBOSE, " ");
398     }
399
400   print_row_stats (dp, dltime, true);
401   logputs (LOG_VERBOSE, "\n\n");
402   log_set_flush (false);
403
404   xfree (dp);
405 }
406
407 /* This function interprets the progress "parameters".  For example,
408    if Wget is invoked with --progress=dot:mega, it will set the
409    "dot-style" to "mega".  Valid styles are default, binary, mega, and
410    giga.  */
411
412 static void
413 dot_set_params (const char *params)
414 {
415   if (!params || !*params)
416     params = opt.dot_style;
417
418   if (!params)
419     return;
420
421   /* We use this to set the retrieval style.  */
422   if (!strcasecmp (params, "default"))
423     {
424       /* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
425          line.  */
426       opt.dot_bytes = 1024;
427       opt.dot_spacing = 10;
428       opt.dots_in_line = 50;
429     }
430   else if (!strcasecmp (params, "binary"))
431     {
432       /* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
433          (384K) in a line.  */
434       opt.dot_bytes = 8192;
435       opt.dot_spacing = 16;
436       opt.dots_in_line = 48;
437     }
438   else if (!strcasecmp (params, "mega"))
439     {
440       /* "Mega" retrieval, for retrieving very long files; each dot is
441          64K, 8 dots in a cluster, 6 clusters (3M) in a line.  */
442       opt.dot_bytes = 65536L;
443       opt.dot_spacing = 8;
444       opt.dots_in_line = 48;
445     }
446   else if (!strcasecmp (params, "giga"))
447     {
448       /* "Giga" retrieval, for retrieving very very *very* long files;
449          each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
450          line.  */
451       opt.dot_bytes = (1L << 20);
452       opt.dot_spacing = 8;
453       opt.dots_in_line = 32;
454     }
455   else
456     fprintf (stderr,
457              _("Invalid dot style specification `%s'; leaving unchanged.\n"),
458              params);
459 }
460 \f
461 /* "Thermometer" (bar) progress. */
462
463 /* Assumed screen width if we can't find the real value.  */
464 #define DEFAULT_SCREEN_WIDTH 80
465
466 /* Minimum screen width we'll try to work with.  If this is too small,
467    create_image will overflow the buffer.  */
468 #define MINIMUM_SCREEN_WIDTH 45
469
470 /* The last known screen width.  This can be updated by the code that
471    detects that SIGWINCH was received (but it's never updated from the
472    signal handler).  */
473 static int screen_width;
474
475 /* A flag that, when set, means SIGWINCH was received.  */
476 static volatile sig_atomic_t received_sigwinch;
477
478 /* Size of the download speed history ring. */
479 #define DLSPEED_HISTORY_SIZE 20
480
481 /* The minimum time length of a history sample.  By default, each
482    sample is at least 150ms long, which means that, over the course of
483    20 samples, "current" download speed spans at least 3s into the
484    past.  */
485 #define DLSPEED_SAMPLE_MIN 0.15
486
487 /* The time after which the download starts to be considered
488    "stalled", i.e. the current bandwidth is not printed and the recent
489    download speeds are scratched.  */
490 #define STALL_START_TIME 5
491
492 /* Time between screen refreshes will not be shorter than this, so
493    that Wget doesn't swamp the TTY with output.  */
494 #define REFRESH_INTERVAL 0.2
495
496 /* Don't refresh the ETA too often to avoid jerkiness in predictions.
497    This allows ETA to change approximately once per second.  */
498 #define ETA_REFRESH_INTERVAL 0.99
499
500 struct bar_progress {
501   wgint initial_length;         /* how many bytes have been downloaded
502                                    previously. */
503   wgint total_length;           /* expected total byte count when the
504                                    download finishes */
505   wgint count;                  /* bytes downloaded so far */
506
507   double last_screen_update;    /* time of the last screen update,
508                                    measured since the beginning of
509                                    download. */
510
511   int width;                    /* screen width we're using at the
512                                    time the progress gauge was
513                                    created.  this is different from
514                                    the screen_width global variable in
515                                    that the latter can be changed by a
516                                    signal. */
517   char *buffer;                 /* buffer where the bar "image" is
518                                    stored. */
519   int tick;                     /* counter used for drawing the
520                                    progress bar where the total size
521                                    is not known. */
522
523   /* The following variables (kept in a struct for namespace reasons)
524      keep track of recent download speeds.  See bar_update() for
525      details.  */
526   struct bar_progress_hist {
527     int pos;
528     double times[DLSPEED_HISTORY_SIZE];
529     wgint bytes[DLSPEED_HISTORY_SIZE];
530
531     /* The sum of times and bytes respectively, maintained for
532        efficiency. */
533     double total_time;
534     wgint total_bytes;
535   } hist;
536
537   double recent_start;          /* timestamp of beginning of current
538                                    position. */
539   wgint recent_bytes;           /* bytes downloaded so far. */
540
541   bool stalled;                 /* set when no data arrives for longer
542                                    than STALL_START_TIME, then reset
543                                    when new data arrives. */
544
545   /* create_image() uses these to make sure that ETA information
546      doesn't flicker. */
547   double last_eta_time;         /* time of the last update to download
548                                    speed and ETA, measured since the
549                                    beginning of download. */
550   int last_eta_value;
551 };
552
553 static void create_image (struct bar_progress *, double, bool);
554 static void display_image (char *);
555
556 static void *
557 bar_create (wgint initial, wgint total)
558 {
559   struct bar_progress *bp = xnew0 (struct bar_progress);
560
561   /* In theory, our callers should take care of this pathological
562      case, but it can sometimes happen. */
563   if (initial > total)
564     total = initial;
565
566   bp->initial_length = initial;
567   bp->total_length   = total;
568
569   /* Initialize screen_width if this hasn't been done or if it might
570      have changed, as indicated by receiving SIGWINCH.  */
571   if (!screen_width || received_sigwinch)
572     {
573       screen_width = determine_screen_width ();
574       if (!screen_width)
575         screen_width = DEFAULT_SCREEN_WIDTH;
576       else if (screen_width < MINIMUM_SCREEN_WIDTH)
577         screen_width = MINIMUM_SCREEN_WIDTH;
578       received_sigwinch = 0;
579     }
580
581   /* - 1 because we don't want to use the last screen column. */
582   bp->width = screen_width - 1;
583   /* + enough space for the terminating zero, and hopefully enough room
584    * for multibyte characters. */
585   bp->buffer = xmalloc (bp->width + 100);
586
587   logputs (LOG_VERBOSE, "\n");
588
589   create_image (bp, 0, false);
590   display_image (bp->buffer);
591
592   return bp;
593 }
594
595 static void update_speed_ring (struct bar_progress *, wgint, double);
596
597 static void
598 bar_update (void *progress, wgint howmuch, double dltime)
599 {
600   struct bar_progress *bp = progress;
601   bool force_screen_update = false;
602
603   bp->count += howmuch;
604   if (bp->total_length > 0
605       && bp->count + bp->initial_length > bp->total_length)
606     /* We could be downloading more than total_length, e.g. when the
607        server sends an incorrect Content-Length header.  In that case,
608        adjust bp->total_length to the new reality, so that the code in
609        create_image() that depends on total size being smaller or
610        equal to the expected size doesn't abort.  */
611     bp->total_length = bp->initial_length + bp->count;
612
613   update_speed_ring (bp, howmuch, dltime);
614
615   /* If SIGWINCH (the window size change signal) been received,
616      determine the new screen size and update the screen.  */
617   if (received_sigwinch)
618     {
619       int old_width = screen_width;
620       screen_width = determine_screen_width ();
621       if (!screen_width)
622         screen_width = DEFAULT_SCREEN_WIDTH;
623       else if (screen_width < MINIMUM_SCREEN_WIDTH)
624         screen_width = MINIMUM_SCREEN_WIDTH;
625       if (screen_width != old_width)
626         {
627           bp->width = screen_width - 1;
628           bp->buffer = xrealloc (bp->buffer, bp->width + 100);
629           force_screen_update = true;
630         }
631       received_sigwinch = 0;
632     }
633
634   if (dltime - bp->last_screen_update < REFRESH_INTERVAL && !force_screen_update)
635     /* Don't update more often than five times per second. */
636     return;
637
638   create_image (bp, dltime, false);
639   display_image (bp->buffer);
640   bp->last_screen_update = dltime;
641 }
642
643 static void
644 bar_finish (void *progress, double dltime)
645 {
646   struct bar_progress *bp = progress;
647
648   if (bp->total_length > 0
649       && bp->count + bp->initial_length > bp->total_length)
650     /* See bar_update() for explanation. */
651     bp->total_length = bp->initial_length + bp->count;
652
653   create_image (bp, dltime, true);
654   display_image (bp->buffer);
655
656   logputs (LOG_VERBOSE, "\n\n");
657
658   xfree (bp->buffer);
659   xfree (bp);
660 }
661
662 /* This code attempts to maintain the notion of a "current" download
663    speed, over the course of no less than 3s.  (Shorter intervals
664    produce very erratic results.)
665
666    To do so, it samples the speed in 150ms intervals and stores the
667    recorded samples in a FIFO history ring.  The ring stores no more
668    than 20 intervals, hence the history covers the period of at least
669    three seconds and at most 20 reads into the past.  This method
670    should produce reasonable results for downloads ranging from very
671    slow to very fast.
672
673    The idea is that for fast downloads, we get the speed over exactly
674    the last three seconds.  For slow downloads (where a network read
675    takes more than 150ms to complete), we get the speed over a larger
676    time period, as large as it takes to complete thirty reads.  This
677    is good because slow downloads tend to fluctuate more and a
678    3-second average would be too erratic.  */
679
680 static void
681 update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime)
682 {
683   struct bar_progress_hist *hist = &bp->hist;
684   double recent_age = dltime - bp->recent_start;
685
686   /* Update the download count. */
687   bp->recent_bytes += howmuch;
688
689   /* For very small time intervals, we return after having updated the
690      "recent" download count.  When its age reaches or exceeds minimum
691      sample time, it will be recorded in the history ring.  */
692   if (recent_age < DLSPEED_SAMPLE_MIN)
693     return;
694
695   if (howmuch == 0)
696     {
697       /* If we're not downloading anything, we might be stalling,
698          i.e. not downloading anything for an extended period of time.
699          Since 0-reads do not enter the history ring, recent_age
700          effectively measures the time since last read.  */
701       if (recent_age >= STALL_START_TIME)
702         {
703           /* If we're stalling, reset the ring contents because it's
704              stale and because it will make bar_update stop printing
705              the (bogus) current bandwidth.  */
706           bp->stalled = true;
707           xzero (*hist);
708           bp->recent_bytes = 0;
709         }
710       return;
711     }
712
713   /* We now have a non-zero amount of to store to the speed ring.  */
714
715   /* If the stall status was acquired, reset it. */
716   if (bp->stalled)
717     {
718       bp->stalled = false;
719       /* "recent_age" includes the the entired stalled period, which
720          could be very long.  Don't update the speed ring with that
721          value because the current bandwidth would start too small.
722          Start with an arbitrary (but more reasonable) time value and
723          let it level out.  */
724       recent_age = 1;
725     }
726
727   /* Store "recent" bytes and download time to history ring at the
728      position POS.  */
729
730   /* To correctly maintain the totals, first invalidate existing data
731      (least recent in time) at this position. */
732   hist->total_time  -= hist->times[hist->pos];
733   hist->total_bytes -= hist->bytes[hist->pos];
734
735   /* Now store the new data and update the totals. */
736   hist->times[hist->pos] = recent_age;
737   hist->bytes[hist->pos] = bp->recent_bytes;
738   hist->total_time  += recent_age;
739   hist->total_bytes += bp->recent_bytes;
740
741   /* Start a new "recent" period. */
742   bp->recent_start = dltime;
743   bp->recent_bytes = 0;
744
745   /* Advance the current ring position. */
746   if (++hist->pos == DLSPEED_HISTORY_SIZE)
747     hist->pos = 0;
748
749 #if 0
750   /* Sledgehammer check to verify that the totals are accurate. */
751   {
752     int i;
753     double sumt = 0, sumb = 0;
754     for (i = 0; i < DLSPEED_HISTORY_SIZE; i++)
755       {
756         sumt += hist->times[i];
757         sumb += hist->bytes[i];
758       }
759     assert (sumb == hist->total_bytes);
760     /* We can't use assert(sumt==hist->total_time) because some
761        precision is lost by adding and subtracting floating-point
762        numbers.  But during a download this precision should not be
763        detectable, i.e. no larger than 1ns.  */
764     double diff = sumt - hist->total_time;
765     if (diff < 0) diff = -diff;
766     assert (diff < 1e-9);
767   }
768 #endif
769 }
770
771 #if USE_NLS_PROGRESS_BAR
772 int
773 count_cols (const char *mbs)
774 {
775   wchar_t wc;
776   int     bytes;
777   int     remaining = strlen(mbs);
778   int     cols = 0;
779   int     wccols;
780
781   while (*mbs != '\0')
782     {
783       bytes = mbtowc (&wc, mbs, remaining);
784       assert (bytes != 0);  /* Only happens when *mbs == '\0' */
785       if (bytes == -1)
786         {
787           /* Invalid sequence. We'll just have to fudge it. */
788           return cols + remaining;
789         }
790       mbs += bytes;
791       remaining -= bytes;
792       wccols = wcwidth(wc);
793       cols += (wccols == -1? 1 : wccols);
794     }
795   return cols;
796 }
797 #else
798 # define count_cols(mbs) ((int)(strlen(mbs)))
799 #endif
800
801 const char *
802 get_eta (int *bcd)
803 {
804   /* Translation note: "ETA" is English-centric, but this must
805      be short, ideally 3 chars.  Abbreviate if necessary.  */
806   static const char eta_str[] = N_("  eta %s");
807   static const char *eta_trans;
808   static int bytes_cols_diff;
809   if (eta_trans == NULL)
810     {
811       int nbytes;
812       int ncols;
813
814 #if USE_NLS_PROGRESS_BAR
815       eta_trans = _(eta_str);
816 #else
817       eta_trans = eta_str;
818 #endif
819
820       /* Determine the number of bytes used in the translated string,
821        * versus the number of columns used. This is to figure out how
822        * many spaces to add at the end to pad to the full line width.
823        *
824        * We'll store the difference between the number of bytes and
825        * number of columns, so that removing this from the string length
826        * will reveal the total number of columns in the progress bar. */
827       nbytes = strlen (eta_trans);
828       ncols = count_cols (eta_trans);
829       bytes_cols_diff = nbytes - ncols;
830     }
831
832   if (bcd != NULL)
833     *bcd = bytes_cols_diff;
834
835   return eta_trans;
836 }
837
838 #define APPEND_LITERAL(s) do {                  \
839   memcpy (p, s, sizeof (s) - 1);                \
840   p += sizeof (s) - 1;                          \
841 } while (0)
842
843 /* Use move_to_end (s) to get S to point the end of the string (the
844    terminating \0).  This is faster than s+=strlen(s), but some people
845    are confused when they see strchr (s, '\0') in the code.  */
846 #define move_to_end(s) s = strchr (s, '\0');
847
848 #ifndef MAX
849 # define MAX(a, b) ((a) >= (b) ? (a) : (b))
850 #endif
851
852 static void
853 create_image (struct bar_progress *bp, double dl_total_time, bool done)
854 {
855   char *p = bp->buffer;
856   wgint size = bp->initial_length + bp->count;
857
858   const char *size_grouped = with_thousand_seps (size);
859   int size_grouped_len = count_cols (size_grouped);
860   /* Difference between num cols and num bytes: */
861   int size_grouped_diff = strlen (size_grouped) - size_grouped_len;
862   int size_grouped_pad; /* Used to pad the field width for size_grouped. */
863
864   struct bar_progress_hist *hist = &bp->hist;
865
866   /* The progress bar should look like this:
867      xx% [=======>             ] nn,nnn 12.34K/s  eta 36m 51s
868
869      Calculate the geometry.  The idea is to assign as much room as
870      possible to the progress bar.  The other idea is to never let
871      things "jitter", i.e. pad elements that vary in size so that
872      their variance does not affect the placement of other elements.
873      It would be especially bad for the progress bar to be resized
874      randomly.
875
876      "xx% " or "100%"  - percentage               - 4 chars
877      "[]"              - progress bar decorations - 2 chars
878      " nnn,nnn,nnn"    - downloaded bytes         - 12 chars or very rarely more
879      " 12.5K/s"        - download rate             - 8 chars
880      "  eta 36m 51s"   - ETA                      - 14 chars
881
882      "=====>..."       - progress bar             - the rest
883   */
884   int dlbytes_size = 1 + MAX (size_grouped_len, 11);
885   int progress_size = bp->width - (4 + 2 + dlbytes_size + 8 + 14);
886
887   /* The difference between the number of bytes used,
888      and the number of columns used. */
889   int bytes_cols_diff = 0;
890
891   if (progress_size < 5)
892     progress_size = 0;
893
894   /* "xx% " */
895   if (bp->total_length > 0)
896     {
897       int percentage = 100.0 * size / bp->total_length;
898       assert (percentage <= 100);
899
900       if (percentage < 100)
901         sprintf (p, "%2d%% ", percentage);
902       else
903         strcpy (p, "100%");
904       p += 4;
905     }
906   else
907     APPEND_LITERAL ("    ");
908
909   /* The progress bar: "[====>      ]" or "[++==>      ]". */
910   if (progress_size && bp->total_length > 0)
911     {
912       /* Size of the initial portion. */
913       int insz = (double)bp->initial_length / bp->total_length * progress_size;
914
915       /* Size of the downloaded portion. */
916       int dlsz = (double)size / bp->total_length * progress_size;
917
918       char *begin;
919       int i;
920
921       assert (dlsz <= progress_size);
922       assert (insz <= dlsz);
923
924       *p++ = '[';
925       begin = p;
926
927       /* Print the initial portion of the download with '+' chars, the
928          rest with '=' and one '>'.  */
929       for (i = 0; i < insz; i++)
930         *p++ = '+';
931       dlsz -= insz;
932       if (dlsz > 0)
933         {
934           for (i = 0; i < dlsz - 1; i++)
935             *p++ = '=';
936           *p++ = '>';
937         }
938
939       while (p - begin < progress_size)
940         *p++ = ' ';
941       *p++ = ']';
942     }
943   else if (progress_size)
944     {
945       /* If we can't draw a real progress bar, then at least show
946          *something* to the user.  */
947       int ind = bp->tick % (progress_size * 2 - 6);
948       int i, pos;
949
950       /* Make the star move in two directions. */
951       if (ind < progress_size - 2)
952         pos = ind + 1;
953       else
954         pos = progress_size - (ind - progress_size + 5);
955
956       *p++ = '[';
957       for (i = 0; i < progress_size; i++)
958         {
959           if      (i == pos - 1) *p++ = '<';
960           else if (i == pos    ) *p++ = '=';
961           else if (i == pos + 1) *p++ = '>';
962           else
963             *p++ = ' ';
964         }
965       *p++ = ']';
966
967       ++bp->tick;
968     }
969
970   /* " 234,567,890" */
971   sprintf (p, " %s", size_grouped);
972   move_to_end (p);
973   /* Pad with spaces to 11 chars for the size_grouped field;
974    * couldn't use the field width specifier in sprintf, because
975    * it counts in bytes, not characters. */
976   for (size_grouped_pad = 11 - size_grouped_len;
977        size_grouped_pad > 0;
978        --size_grouped_pad)
979     {
980       *p++ = ' ';
981     }
982
983   /* " 12.52K/s" */
984   if (hist->total_time > 0 && hist->total_bytes)
985     {
986       static const char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
987       int units = 0;
988       /* Calculate the download speed using the history ring and
989          recent data that hasn't made it to the ring yet.  */
990       wgint dlquant = hist->total_bytes + bp->recent_bytes;
991       double dltime = hist->total_time + (dl_total_time - bp->recent_start);
992       double dlspeed = calc_rate (dlquant, dltime, &units);
993       sprintf (p, " %4.*f%s", dlspeed >= 99.95 ? 0 : dlspeed >= 9.995 ? 1 : 2,
994                dlspeed, short_units[units]);
995       move_to_end (p);
996     }
997   else
998     APPEND_LITERAL (" --.-K/s");
999
1000   if (!done)
1001     {
1002       /* "  eta ..m ..s"; wait for three seconds before displaying the ETA.
1003          That's because the ETA value needs a while to become
1004          reliable.  */
1005       if (bp->total_length > 0 && bp->count > 0 && dl_total_time > 3)
1006         {
1007           int eta;
1008
1009           /* Don't change the value of ETA more than approximately once
1010              per second; doing so would cause flashing without providing
1011              any value to the user. */
1012           if (bp->total_length != size
1013               && bp->last_eta_value != 0
1014               && dl_total_time - bp->last_eta_time < ETA_REFRESH_INTERVAL)
1015             eta = bp->last_eta_value;
1016           else
1017             {
1018               /* Calculate ETA using the average download speed to predict
1019                  the future speed.  If you want to use a speed averaged
1020                  over a more recent period, replace dl_total_time with
1021                  hist->total_time and bp->count with hist->total_bytes.
1022                  I found that doing that results in a very jerky and
1023                  ultimately unreliable ETA.  */
1024               wgint bytes_remaining = bp->total_length - size;
1025               double eta_ = dl_total_time * bytes_remaining / bp->count;
1026               if (eta_ >= INT_MAX - 1)
1027                 goto skip_eta;
1028               eta = (int) (eta_ + 0.5);
1029               bp->last_eta_value = eta;
1030               bp->last_eta_time = dl_total_time;
1031             }
1032
1033           sprintf (p, get_eta(&bytes_cols_diff),
1034                    eta_to_human_short (eta, false));
1035           move_to_end (p);
1036         }
1037       else if (bp->total_length > 0)
1038         {
1039         skip_eta:
1040           APPEND_LITERAL ("             ");
1041         }
1042     }
1043   else
1044     {
1045       /* When the download is done, print the elapsed time.  */
1046       int nbytes;
1047       int ncols;
1048
1049       /* Note to translators: this should not take up more room than
1050          available here.  Abbreviate if necessary.  */
1051       strcpy (p, _("   in "));
1052       nbytes = strlen (p);
1053       ncols  = count_cols (p);
1054       bytes_cols_diff = nbytes - ncols;
1055       p += nbytes;
1056       if (dl_total_time >= 10)
1057         strcpy (p, eta_to_human_short ((int) (dl_total_time + 0.5), false));
1058       else
1059         sprintf (p, "%ss", print_decimal (dl_total_time));
1060       move_to_end (p);
1061     }
1062
1063   while (p - bp->buffer - bytes_cols_diff - size_grouped_diff < bp->width)
1064     *p++ = ' ';
1065   *p = '\0';
1066 }
1067
1068 /* Print the contents of the buffer as a one-line ASCII "image" so
1069    that it can be overwritten next time.  */
1070
1071 static void
1072 display_image (char *buf)
1073 {
1074   bool old = log_set_save_context (false);
1075   logputs (LOG_VERBOSE, "\r");
1076   logputs (LOG_VERBOSE, buf);
1077   log_set_save_context (old);
1078 }
1079
1080 static void
1081 bar_set_params (const char *params)
1082 {
1083   char *term = getenv ("TERM");
1084
1085   if (params
1086       && 0 == strcmp (params, "force"))
1087     current_impl_locked = 1;
1088
1089   if ((opt.lfilename
1090 #ifdef HAVE_ISATTY
1091        /* The progress bar doesn't make sense if the output is not a
1092           TTY -- when logging to file, it is better to review the
1093           dots.  */
1094        || !isatty (fileno (stderr))
1095 #endif
1096        /* Normally we don't depend on terminal type because the
1097           progress bar only uses ^M to move the cursor to the
1098           beginning of line, which works even on dumb terminals.  But
1099           Jamie Zawinski reports that ^M and ^H tricks don't work in
1100           Emacs shell buffers, and only make a mess.  */
1101        || (term && 0 == strcmp (term, "emacs"))
1102        )
1103       && !current_impl_locked)
1104     {
1105       /* We're not printing to a TTY, so revert to the fallback
1106          display.  #### We're recursively calling
1107          set_progress_implementation here, which is slightly kludgy.
1108          It would be nicer if we provided that function a return value
1109          indicating a failure of some sort.  */
1110       set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
1111       return;
1112     }
1113 }
1114
1115 #ifdef SIGWINCH
1116 void
1117 progress_handle_sigwinch (int sig)
1118 {
1119   received_sigwinch = 1;
1120   signal (SIGWINCH, progress_handle_sigwinch);
1121 }
1122 #endif
1123
1124 /* Provide a short human-readable rendition of the ETA.  This is like
1125    secs_to_human_time in main.c, except the output doesn't include
1126    fractions (which would look silly in by nature imprecise ETA) and
1127    takes less room.  If the time is measured in hours, hours and
1128    minutes (but not seconds) are shown; if measured in days, then days
1129    and hours are shown.  This ensures brevity while still displaying
1130    as much as possible.
1131
1132    If CONDENSED is true, the separator between minutes and seconds
1133    (and hours and minutes, etc.) is not included, shortening the
1134    display by one additional character.  This is used for dot
1135    progress.
1136
1137    The display never occupies more than 7 characters of screen
1138    space.  */
1139
1140 static const char *
1141 eta_to_human_short (int secs, bool condensed)
1142 {
1143   static char buf[10];          /* 8 should be enough, but just in case */
1144   static int last = -1;
1145   const char *space = condensed ? "" : " ";
1146
1147   /* Trivial optimization.  create_image can call us every 200 msecs
1148      (see bar_update) for fast downloads, but ETA will only change
1149      once per 900 msecs.  */
1150   if (secs == last)
1151     return buf;
1152   last = secs;
1153
1154   if (secs < 100)
1155     sprintf (buf, "%ds", secs);
1156   else if (secs < 100 * 60)
1157     sprintf (buf, "%dm%s%ds", secs / 60, space, secs % 60);
1158   else if (secs < 48 * 3600)
1159     sprintf (buf, "%dh%s%dm", secs / 3600, space, (secs / 60) % 60);
1160   else if (secs < 100 * 86400)
1161     sprintf (buf, "%dd%s%dh", secs / 86400, space, (secs / 3600) % 60);
1162   else
1163     /* even (2^31-1)/86400 doesn't overflow BUF. */
1164     sprintf (buf, "%dd", secs / 86400);
1165
1166   return buf;
1167 }