diff options
Diffstat (limited to 'libbb/progress.c')
-rw-r--r-- | libbb/progress.c | 179 |
1 files changed, 116 insertions, 63 deletions
diff --git a/libbb/progress.c b/libbb/progress.c index 40608b047..df43dad5c 100644 --- a/libbb/progress.c +++ b/libbb/progress.c | |||
@@ -52,101 +52,154 @@ static unsigned int get_tty2_width(void) | |||
52 | return width; | 52 | return width; |
53 | } | 53 | } |
54 | 54 | ||
55 | void FAST_FUNC bb_progress_init(bb_progress_t *p) | 55 | void FAST_FUNC bb_progress_init(bb_progress_t *p, const char *curfile) |
56 | { | 56 | { |
57 | #if ENABLE_UNICODE_SUPPORT | ||
58 | init_unicode(); | ||
59 | p->curfile = unicode_conv_to_printable_fixedwidth(/*NULL,*/ curfile, 20); | ||
60 | #else | ||
61 | p->curfile = curfile; | ||
62 | #endif | ||
57 | p->start_sec = monotonic_sec(); | 63 | p->start_sec = monotonic_sec(); |
58 | p->lastupdate_sec = p->start_sec; | 64 | p->last_update_sec = p->start_sec; |
59 | p->lastsize = 0; | 65 | p->last_change_sec = p->start_sec; |
60 | p->inited = 1; | 66 | p->last_size = 0; |
61 | } | 67 | } |
62 | 68 | ||
69 | /* File already had beg_size bytes. | ||
70 | * Then we started downloading. | ||
71 | * We downloaded "transferred" bytes so far. | ||
72 | * Download is expected to stop when total size (beg_size + transferred) | ||
73 | * will be "totalsize" bytes. | ||
74 | * If totalsize == 0, then it is unknown. | ||
75 | */ | ||
63 | void FAST_FUNC bb_progress_update(bb_progress_t *p, | 76 | void FAST_FUNC bb_progress_update(bb_progress_t *p, |
64 | const char *curfile, | 77 | uoff_t beg_size, |
65 | off_t beg_range, | 78 | uoff_t transferred, |
66 | off_t transferred, | 79 | uoff_t totalsize) |
67 | off_t totalsize) | ||
68 | { | 80 | { |
69 | uoff_t beg_and_transferred; | 81 | uoff_t beg_and_transferred; |
70 | unsigned since_last_update, elapsed; | 82 | unsigned since_last_update, elapsed; |
71 | unsigned ratio; | 83 | int barlength; |
72 | int barlength, i; | 84 | int kiloscale; |
73 | 85 | ||
74 | /* totalsize == 0 if it is unknown */ | 86 | //transferred = 1234; /* use for stall detection testing */ |
87 | //totalsize = 0; /* use for unknown size download testing */ | ||
75 | 88 | ||
76 | elapsed = monotonic_sec(); | 89 | elapsed = monotonic_sec(); |
77 | since_last_update = elapsed - p->lastupdate_sec; | 90 | since_last_update = elapsed - p->last_update_sec; |
78 | /* Do not update on every call | 91 | p->last_update_sec = elapsed; |
79 | * (we can be called on every network read!) */ | ||
80 | if (since_last_update == 0 && !totalsize) | ||
81 | return; | ||
82 | 92 | ||
83 | beg_and_transferred = beg_range + transferred; | 93 | if (totalsize != 0 && transferred >= totalsize - beg_size) { |
84 | ratio = 100; | 94 | /* Last call. Do not skip this update */ |
85 | if (beg_and_transferred < totalsize) { | 95 | transferred = totalsize - beg_size; /* sanitize just in case */ |
86 | /* Do not update on every call | 96 | } |
87 | * (we can be called on every network read!) */ | 97 | else if (since_last_update == 0) { |
88 | if (since_last_update == 0) | 98 | /* |
89 | return; | 99 | * Do not update on every call |
90 | /* long long helps to have it working even if !LFS */ | 100 | * (we can be called on every network read!) |
91 | ratio = 100ULL * beg_and_transferred / (uoff_t)totalsize; | 101 | */ |
102 | return; | ||
92 | } | 103 | } |
93 | 104 | ||
94 | #if ENABLE_UNICODE_SUPPORT | 105 | kiloscale = 0; |
95 | init_unicode(); | 106 | /* |
96 | { | 107 | * Scale sizes down if they are close to overflowing. |
97 | char *buf = unicode_conv_to_printable_fixedwidth(/*NULL,*/ curfile, 20); | 108 | * This allows calculations like (100 * transferred / totalsize) |
98 | fprintf(stderr, "\r%s%4u%% ", buf, ratio); | 109 | * without risking overflow: we guarantee 10 highest bits to be 0. |
99 | free(buf); | 110 | * Introduced error is less than 1 / 2^12 ~= 0.025% |
111 | */ | ||
112 | if (ULONG_MAX > 0xffffffff || sizeof(off_t) == 4 || sizeof(off_t) != 8) { | ||
113 | /* | ||
114 | * 64-bit CPU || small off_t: in either case, | ||
115 | * >> is cheap, single-word operation. | ||
116 | * ... || strange off_t: also use this code | ||
117 | * (it is safe, just suboptimal wrt code size), | ||
118 | * because 32/64 optimized one works only for 64-bit off_t. | ||
119 | */ | ||
120 | if (totalsize >= (1 << 22)) { | ||
121 | totalsize >>= 10; | ||
122 | beg_size >>= 10; | ||
123 | transferred >>= 10; | ||
124 | kiloscale = 1; | ||
125 | } | ||
126 | } else { | ||
127 | /* 32-bit CPU and 64-bit off_t. | ||
128 | * Use a 40-bit shift, it is easier to do on 32-bit CPU. | ||
129 | */ | ||
130 | if (totalsize >= (uoff_t)(1ULL << 54)) { | ||
131 | totalsize = (uint32_t)(totalsize >> 32) >> 8; | ||
132 | beg_size = (uint32_t)(beg_size >> 32) >> 8; | ||
133 | transferred = (uint32_t)(transferred >> 32) >> 8; | ||
134 | kiloscale = 4; | ||
135 | } | ||
100 | } | 136 | } |
101 | #else | ||
102 | fprintf(stderr, "\r%-20.20s%4u%% ", curfile, ratio); | ||
103 | #endif | ||
104 | 137 | ||
105 | barlength = get_tty2_width() - 49; | 138 | if (ENABLE_UNICODE_SUPPORT) |
106 | if (barlength > 0) { | 139 | fprintf(stderr, "\r%s", p->curfile); |
107 | /* god bless gcc for variable arrays :) */ | 140 | else |
108 | char buf[barlength + 1]; | 141 | fprintf(stderr, "\r%-20.20s", p->curfile); |
109 | unsigned stars = (unsigned)barlength * ratio / (unsigned)100; | 142 | |
110 | memset(buf, ' ', barlength); | 143 | beg_and_transferred = beg_size + transferred; |
111 | buf[barlength] = '\0'; | 144 | |
112 | memset(buf, '*', stars); | 145 | if (totalsize != 0) { |
113 | fprintf(stderr, "|%s|", buf); | 146 | unsigned ratio = 100 * beg_and_transferred / totalsize; |
147 | fprintf(stderr, "%4u%%", ratio); | ||
148 | |||
149 | barlength = get_tty2_width() - 49; | ||
150 | if (barlength > 0) { | ||
151 | /* god bless gcc for variable arrays :) */ | ||
152 | char buf[barlength + 1]; | ||
153 | unsigned stars = (unsigned)barlength * beg_and_transferred / totalsize; | ||
154 | memset(buf, ' ', barlength); | ||
155 | buf[barlength] = '\0'; | ||
156 | memset(buf, '*', stars); | ||
157 | fprintf(stderr, " |%s|", buf); | ||
158 | } | ||
114 | } | 159 | } |
115 | 160 | ||
116 | i = 0; | ||
117 | while (beg_and_transferred >= 100000) { | 161 | while (beg_and_transferred >= 100000) { |
118 | i++; | ||
119 | beg_and_transferred >>= 10; | 162 | beg_and_transferred >>= 10; |
163 | kiloscale++; | ||
120 | } | 164 | } |
121 | /* see http://en.wikipedia.org/wiki/Tera */ | 165 | /* see http://en.wikipedia.org/wiki/Tera */ |
122 | fprintf(stderr, "%6u%c ", (unsigned)beg_and_transferred, " kMGTPEZY"[i]); | 166 | fprintf(stderr, "%6u%c", (unsigned)beg_and_transferred, " kMGTPEZY"[kiloscale]); |
123 | #define beg_and_transferred dont_use_beg_and_transferred_below | 167 | #define beg_and_transferred dont_use_beg_and_transferred_below() |
124 | 168 | ||
125 | if (transferred > p->lastsize) { | 169 | since_last_update = elapsed - p->last_change_sec; |
126 | p->lastupdate_sec = elapsed; | 170 | if ((unsigned)transferred != p->last_size) { |
127 | p->lastsize = transferred; | 171 | p->last_change_sec = elapsed; |
172 | p->last_size = (unsigned)transferred; | ||
128 | if (since_last_update >= STALLTIME) { | 173 | if (since_last_update >= STALLTIME) { |
129 | /* We "cut off" these seconds from elapsed time | 174 | /* We "cut out" these seconds from elapsed time |
130 | * by adjusting start time */ | 175 | * by adjusting start time */ |
131 | p->start_sec += since_last_update; | 176 | p->start_sec += since_last_update; |
132 | } | 177 | } |
133 | since_last_update = 0; /* we are un-stalled now */ | 178 | since_last_update = 0; /* we are un-stalled now */ |
134 | } | 179 | } |
180 | |||
135 | elapsed -= p->start_sec; /* now it's "elapsed since start" */ | 181 | elapsed -= p->start_sec; /* now it's "elapsed since start" */ |
136 | 182 | ||
137 | if (since_last_update >= STALLTIME) { | 183 | if (since_last_update >= STALLTIME) { |
138 | fprintf(stderr, " - stalled -"); | 184 | fprintf(stderr, " - stalled -"); |
185 | } else if (!totalsize || !transferred || (int)elapsed < 0) { | ||
186 | fprintf(stderr, " --:--:-- ETA"); | ||
139 | } else { | 187 | } else { |
140 | off_t to_download = totalsize - beg_range; | 188 | unsigned eta, secs, hours; |
141 | if (!totalsize || transferred <= 0 || (int)elapsed <= 0 || transferred > to_download) { | 189 | |
142 | fprintf(stderr, "--:--:-- ETA"); | 190 | totalsize -= beg_size; /* now it's "total to upload" */ |
143 | } else { | 191 | |
144 | /* to_download / (transferred/elapsed) - elapsed: */ | 192 | /* Estimated remaining time = |
145 | /* (long long helps to have working ETA even if !LFS) */ | 193 | * estimated_sec_to_dl_totalsize_bytes - elapsed_sec = |
146 | unsigned eta = (unsigned long long)to_download*elapsed/(uoff_t)transferred - elapsed; | 194 | * totalsize / average_bytes_sec_so_far - elapsed = |
147 | unsigned secs = eta % 3600; | 195 | * totalsize / (transferred/elapsed) - elapsed = |
148 | unsigned hours = eta / 3600; | 196 | * totalsize * elapsed / transferred - elapsed |
149 | fprintf(stderr, "%02u:%02u:%02u ETA", hours, secs / 60, secs % 60); | 197 | */ |
150 | } | 198 | eta = totalsize * elapsed / transferred - elapsed; |
199 | if (eta >= 1000*60*60) | ||
200 | eta = 1000*60*60 - 1; | ||
201 | secs = eta % 3600; | ||
202 | hours = eta / 3600; | ||
203 | fprintf(stderr, "%3u:%02u:%02u ETA", hours, secs / 60, secs % 60); | ||
151 | } | 204 | } |
152 | } | 205 | } |