1/* vi: set sw=4 ts=4: */ 2/* 3 * Progress bar code. 4 */ 5/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff, 6 * much of which was blatantly stolen from openssh. 7 */ 8/*- 9 * Copyright (c) 1992, 1993 10 * The Regents of the University of California. All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * 3. BSD Advertising Clause omitted per the July 22, 1999 licensing change 22 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change 23 * 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 */ 40#include "libbb.h" 41#include "unicode.h" 42 43enum { 44 /* Seconds when xfer considered "stalled" */ 45 STALLTIME = 5 46}; 47 48void FAST_FUNC bb_progress_init(bb_progress_t *p, const char *curfile) 49{ 50#if ENABLE_UNICODE_SUPPORT 51 init_unicode(); 52 p->curfile = unicode_conv_to_printable_fixedwidth(/*NULL,*/ curfile, 20); 53#else 54 p->curfile = curfile; 55#endif 56 p->start_sec = monotonic_sec(); 57 p->last_update_sec = p->start_sec; 58 p->last_change_sec = p->start_sec; 59 p->last_size = 0; 60} 61 62/* File already had beg_size bytes. 63 * Then we started downloading. 64 * We downloaded "transferred" bytes so far. 65 * Download is expected to stop when total size (beg_size + transferred) 66 * will be "totalsize" bytes. 67 * If totalsize == 0, then it is unknown. 68 */ 69void FAST_FUNC bb_progress_update(bb_progress_t *p, 70 uoff_t beg_size, 71 uoff_t transferred, 72 uoff_t totalsize) 73{ 74 uoff_t beg_and_transferred; 75 unsigned since_last_update, elapsed; 76 int notty; 77 int kiloscale; 78 79 //transferred = 1234; /* use for stall detection testing */ 80 //totalsize = 0; /* use for unknown size download testing */ 81 82 elapsed = monotonic_sec(); 83 since_last_update = elapsed - p->last_update_sec; 84 p->last_update_sec = elapsed; 85 86 if (totalsize != 0 && transferred >= totalsize - beg_size) { 87 /* Last call. Do not skip this update */ 88 transferred = totalsize - beg_size; /* sanitize just in case */ 89 } 90 else if (since_last_update == 0) { 91 /* 92 * Do not update on every call 93 * (we can be called on every network read!) 94 */ 95 return; 96 } 97 98 kiloscale = 0; 99 /* 100 * Scale sizes down if they are close to overflowing. 101 * This allows calculations like (100 * transferred / totalsize) 102 * without risking overflow: we guarantee 10 highest bits to be 0. 103 * Introduced error is less than 1 / 2^12 ~= 0.025% 104 */ 105 if (ULONG_MAX > 0xffffffff || sizeof(off_t) == 4 || sizeof(off_t) != 8) { 106 /* 107 * 64-bit CPU || small off_t: in either case, 108 * >> is cheap, single-word operation. 109 * ... || strange off_t: also use this code 110 * (it is safe, just suboptimal wrt code size), 111 * because 32/64 optimized one works only for 64-bit off_t. 112 */ 113 if (totalsize >= (1 << 22)) { 114 totalsize >>= 10; 115 beg_size >>= 10; 116 transferred >>= 10; 117 kiloscale = 1; 118 } 119 } else { 120 /* 32-bit CPU and 64-bit off_t. 121 * Use a 40-bit shift, it is easier to do on 32-bit CPU. 122 */ 123/* ONE suppresses "warning: shift count >= width of type" */ 124#define ONE (sizeof(off_t) > 4) 125 if (totalsize >= (uoff_t)(1ULL << 54*ONE)) { 126 totalsize = (uint32_t)(totalsize >> 32*ONE) >> 8; 127 beg_size = (uint32_t)(beg_size >> 32*ONE) >> 8; 128 transferred = (uint32_t)(transferred >> 32*ONE) >> 8; 129 kiloscale = 4; 130 } 131 } 132 133 notty = !isatty(STDERR_FILENO); 134 135 if (ENABLE_UNICODE_SUPPORT) 136 fprintf(stderr, "\r%s" + notty, p->curfile); 137 else 138 fprintf(stderr, "\r%-20.20s" + notty, p->curfile); 139 140 beg_and_transferred = beg_size + transferred; 141 142 if (totalsize != 0) { 143 int barlength; 144 unsigned ratio = 100 * beg_and_transferred / totalsize; 145 fprintf(stderr, "%4u%%", ratio); 146 147 barlength = get_terminal_width(2) - 49; 148 if (barlength > 0) { 149 /* god bless gcc for variable arrays :) */ 150 char buf[barlength + 1]; 151 unsigned stars = (unsigned)barlength * beg_and_transferred / totalsize; 152 memset(buf, ' ', barlength); 153 buf[barlength] = '\0'; 154 memset(buf, '*', stars); 155 fprintf(stderr, " |%s|", buf); 156 } 157 } 158 159 while (beg_and_transferred >= 100000) { 160 beg_and_transferred >>= 10; 161 kiloscale++; 162 } 163 /* see http://en.wikipedia.org/wiki/Tera */ 164 fprintf(stderr, "%6u%c", (unsigned)beg_and_transferred, " kMGTPEZY"[kiloscale]); 165#define beg_and_transferred dont_use_beg_and_transferred_below() 166 167 since_last_update = elapsed - p->last_change_sec; 168 if ((unsigned)transferred != p->last_size) { 169 p->last_change_sec = elapsed; 170 p->last_size = (unsigned)transferred; 171 if (since_last_update >= STALLTIME) { 172 /* We "cut out" these seconds from elapsed time 173 * by adjusting start time */ 174 p->start_sec += since_last_update; 175 } 176 since_last_update = 0; /* we are un-stalled now */ 177 } 178 179 elapsed -= p->start_sec; /* now it's "elapsed since start" */ 180 181 if (since_last_update >= STALLTIME) { 182 fprintf(stderr, " - stalled -"); 183 } else if (!totalsize || !transferred || (int)elapsed < 0) { 184 fprintf(stderr, " --:--:-- ETA"); 185 } else { 186 unsigned eta, secs, hours; 187 188 totalsize -= beg_size; /* now it's "total to upload" */ 189 190 /* Estimated remaining time = 191 * estimated_sec_to_dl_totalsize_bytes - elapsed_sec = 192 * totalsize / average_bytes_sec_so_far - elapsed = 193 * totalsize / (transferred/elapsed) - elapsed = 194 * totalsize * elapsed / transferred - elapsed 195 */ 196 eta = totalsize * elapsed / transferred - elapsed; 197 if (eta >= 1000*60*60) 198 eta = 1000*60*60 - 1; 199 secs = eta % 3600; 200 hours = eta / 3600; 201 fprintf(stderr, "%3u:%02u:%02u ETA", hours, secs / 60, secs % 60); 202 } 203 if (notty) 204 fputc('\n', stderr); 205} 206