Bump to version 1.22.1
[platform/upstream/busybox.git] / libbb / progress.c
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
43 enum {
44         /* Seconds when xfer considered "stalled" */
45         STALLTIME = 5
46 };
47
48 static unsigned int get_tty2_width(void)
49 {
50         unsigned width;
51         get_terminal_width_height(2, &width, NULL);
52         return width;
53 }
54
55 void FAST_FUNC bb_progress_init(bb_progress_t *p, const char *curfile)
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
63         p->start_sec = monotonic_sec();
64         p->last_update_sec = p->start_sec;
65         p->last_change_sec = p->start_sec;
66         p->last_size = 0;
67 }
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  */
76 void FAST_FUNC bb_progress_update(bb_progress_t *p,
77                 uoff_t beg_size,
78                 uoff_t transferred,
79                 uoff_t totalsize)
80 {
81         uoff_t beg_and_transferred;
82         unsigned since_last_update, elapsed;
83         int barlength;
84         int kiloscale;
85
86         //transferred = 1234; /* use for stall detection testing */
87         //totalsize = 0; /* use for unknown size download testing */
88
89         elapsed = monotonic_sec();
90         since_last_update = elapsed - p->last_update_sec;
91         p->last_update_sec = elapsed;
92
93         if (totalsize != 0 && transferred >= totalsize - beg_size) {
94                 /* Last call. Do not skip this update */
95                 transferred = totalsize - beg_size; /* sanitize just in case */
96         }
97         else if (since_last_update == 0) {
98                 /*
99                  * Do not update on every call
100                  * (we can be called on every network read!)
101                  */
102                 return;
103         }
104
105         kiloscale = 0;
106         /*
107          * Scale sizes down if they are close to overflowing.
108          * This allows calculations like (100 * transferred / totalsize)
109          * without risking overflow: we guarantee 10 highest bits to be 0.
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 /* ONE suppresses "warning: shift count >= width of type" */
131 #define ONE (sizeof(off_t) > 4)
132                 if (totalsize >= (uoff_t)(1ULL << 54*ONE)) {
133                         totalsize = (uint32_t)(totalsize >> 32*ONE) >> 8;
134                         beg_size = (uint32_t)(beg_size >> 32*ONE) >> 8;
135                         transferred = (uint32_t)(transferred >> 32*ONE) >> 8;
136                         kiloscale = 4;
137                 }
138         }
139
140         if (ENABLE_UNICODE_SUPPORT)
141                 fprintf(stderr, "\r%s", p->curfile);
142         else
143                 fprintf(stderr, "\r%-20.20s", p->curfile);
144
145         beg_and_transferred = beg_size + transferred;
146
147         if (totalsize != 0) {
148                 unsigned ratio = 100 * beg_and_transferred / totalsize;
149                 fprintf(stderr, "%4u%%", ratio);
150
151                 barlength = get_tty2_width() - 49;
152                 if (barlength > 0) {
153                         /* god bless gcc for variable arrays :) */
154                         char buf[barlength + 1];
155                         unsigned stars = (unsigned)barlength * beg_and_transferred / totalsize;
156                         memset(buf, ' ', barlength);
157                         buf[barlength] = '\0';
158                         memset(buf, '*', stars);
159                         fprintf(stderr, " |%s|", buf);
160                 }
161         }
162
163         while (beg_and_transferred >= 100000) {
164                 beg_and_transferred >>= 10;
165                 kiloscale++;
166         }
167         /* see http://en.wikipedia.org/wiki/Tera */
168         fprintf(stderr, "%6u%c", (unsigned)beg_and_transferred, " kMGTPEZY"[kiloscale]);
169 #define beg_and_transferred dont_use_beg_and_transferred_below()
170
171         since_last_update = elapsed - p->last_change_sec;
172         if ((unsigned)transferred != p->last_size) {
173                 p->last_change_sec = elapsed;
174                 p->last_size = (unsigned)transferred;
175                 if (since_last_update >= STALLTIME) {
176                         /* We "cut out" these seconds from elapsed time
177                          * by adjusting start time */
178                         p->start_sec += since_last_update;
179                 }
180                 since_last_update = 0; /* we are un-stalled now */
181         }
182
183         elapsed -= p->start_sec; /* now it's "elapsed since start" */
184
185         if (since_last_update >= STALLTIME) {
186                 fprintf(stderr, "  - stalled -");
187         } else if (!totalsize || !transferred || (int)elapsed < 0) {
188                 fprintf(stderr, " --:--:-- ETA");
189         } else {
190                 unsigned eta, secs, hours;
191
192                 totalsize -= beg_size; /* now it's "total to upload" */
193
194                 /* Estimated remaining time =
195                  * estimated_sec_to_dl_totalsize_bytes - elapsed_sec =
196                  * totalsize / average_bytes_sec_so_far - elapsed =
197                  * totalsize / (transferred/elapsed) - elapsed =
198                  * totalsize * elapsed / transferred - elapsed
199                  */
200                 eta = totalsize * elapsed / transferred - elapsed;
201                 if (eta >= 1000*60*60)
202                         eta = 1000*60*60 - 1;
203                 secs = eta % 3600;
204                 hours = eta / 3600;
205                 fprintf(stderr, "%3u:%02u:%02u ETA", hours, secs / 60, secs % 60);
206         }
207 }