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