summaryrefslogtreecommitdiff
path: root/progressmeter.c
diff options
context:
space:
mode:
authorDarren Tucker <dtucker@zip.com.au>2003-08-02 23:28:38 +1000
committerDarren Tucker <dtucker@zip.com.au>2003-08-02 23:28:38 +1000
commit06ef75bf0c2449ab4880f6ff40729497782e6e60 (patch)
tree0ef158bf3aeac04ad016acce2a0623d6548e9ad8 /progressmeter.c
parent019cefeaadc06a3664076cae10aedae4aed13911 (diff)
- markus@cvs.openbsd.org 2003/07/29 18:24:00
[LICENCE progressmeter.c] replace 4 clause BSD licensed progressmeter code with a replacement from Nils Nordman and myself; ok deraadt@ (copied from OpenBSD an re-applied portable changes)
Diffstat (limited to 'progressmeter.c')
-rw-r--r--progressmeter.c407
1 files changed, 200 insertions, 207 deletions
diff --git a/progressmeter.c b/progressmeter.c
index 6aa225adf..170d869f4 100644
--- a/progressmeter.c
+++ b/progressmeter.c
@@ -1,6 +1,5 @@
1/* 1/*
2 * Copyright (c) 1999 Theo de Raadt. All rights reserved. 2 * Copyright (c) 2003 Nils Nordman. All rights reserved.
3 * Copyright (c) 1999 Aaron Campbell. All rights reserved.
4 * 3 *
5 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
@@ -23,248 +22,242 @@
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */ 23 */
25 24
26/*
27 * Copyright (c) 1997-2003 The NetBSD Foundation, Inc.
28 * All rights reserved.
29 *
30 * This code is derived from software contributed to The NetBSD Foundation
31 * by Luke Mewburn.
32 *
33 * This code is derived from software contributed to The NetBSD Foundation
34 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
35 * NASA Ames Research Center.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
45 * 3. All advertising materials mentioning features or use of this software
46 * must display the following acknowledgement:
47 * This product includes software developed by the NetBSD
48 * Foundation, Inc. and its contributors.
49 * 4. Neither the name of The NetBSD Foundation nor the names of its
50 * contributors may be used to endorse or promote products derived
51 * from this software without specific prior written permission.
52 *
53 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
54 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
55 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
56 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
57 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
58 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
59 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
60 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
61 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
62 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
63 * POSSIBILITY OF SUCH DAMAGE.
64 */
65
66#include "includes.h" 25#include "includes.h"
67RCSID("$OpenBSD: progressmeter.c,v 1.8 2003/06/28 16:23:06 deraadt Exp $"); 26RCSID("$OpenBSD: progressmeter.c,v 1.13 2003/07/31 22:34:03 markus Exp $");
68
69#ifdef HAVE_LIBGEN_H
70#include <libgen.h>
71#endif
72 27
73#include "atomicio.h"
74#include "progressmeter.h" 28#include "progressmeter.h"
29#include "atomicio.h"
75#include "misc.h" 30#include "misc.h"
76 31
77/* Number of seconds before xfer considered "stalled". */ 32#define DEFAULT_WINSIZE 80
78#define STALLTIME 5 33#define MAX_WINSIZE 512
79/* alarm() interval for updating progress meter. */ 34#define PADDING 1 /* padding between the progress indicators */
80#define PROGRESSTIME 1 35#define UPDATE_INTERVAL 1 /* update the progress meter every second */
36#define STALL_TIME 5 /* we're stalled after this many seconds */
81 37
82/* Signal handler used for updating the progress meter. */ 38/* determines whether we can output to the terminal */
83static void update_progress_meter(int); 39static int can_output(void);
84 40
85/* Returns non-zero if we are the foreground process. */ 41/* formats and inserts the specified size into the given buffer */
86static int foregroundproc(void); 42static void format_size(char *, int, off_t);
43static void format_rate(char *, int, off_t);
87 44
88/* Returns width of the terminal (for progress meter calculations). */ 45/* updates the progressmeter to reflect the current state of the transfer */
89static int get_tty_width(void); 46void refresh_progress_meter(void);
90 47
91/* Visual statistics about files as they are transferred. */ 48/* signal handler for updating the progress meter */
92static void draw_progress_meter(void); 49static void update_progress_meter(int);
93
94/* Time a transfer started. */
95static struct timeval start;
96 50
97/* Number of bytes of current file transferred so far. */ 51static time_t start; /* start progress */
98static volatile off_t *statbytes; 52static time_t last_update; /* last progress update */
53static char *file; /* name of the file being transferred */
54static off_t end_pos; /* ending position of transfer */
55static off_t cur_pos; /* transfer position as of last refresh */
56static volatile off_t *counter; /* progress counter */
57static long stalled; /* how long we have been stalled */
58static int bytes_per_second; /* current speed in bytes per second */
59static int win_size; /* terminal window size */
99 60
100/* Total size of current file. */ 61/* units for format_size */
101static off_t totalbytes; 62static const char unit[] = " KMGT";
102 63
103/* Name of current file being transferred. */ 64static int
104static char *curfile; 65can_output(void)
66{
67 return (getpgrp() == tcgetpgrp(STDOUT_FILENO));
68}
105 69
106/* Time of last update. */ 70static void
107static struct timeval lastupdate; 71format_rate(char *buf, int size, off_t bytes)
72{
73 int i;
74
75 bytes *= 100;
76 for (i = 0; bytes >= 100*1000 && unit[i] != 'T'; i++)
77 bytes = (bytes + 512) / 1024;
78 if (i == 0) {
79 i++;
80 bytes = (bytes + 512) / 1024;
81 }
82 snprintf(buf, size, "%3lld.%1lld%c%s",
83 (int64_t) bytes / 100,
84 (int64_t) (bytes + 5) / 10 % 10,
85 unit[i],
86 i ? "B" : " ");
87}
108 88
109/* Size at the time of the last update. */ 89static void
110static off_t lastsize; 90format_size(char *buf, int size, off_t bytes)
91{
92 int i;
93
94 for (i = 0; bytes >= 10000 && unit[i] != 'T'; i++)
95 bytes = (bytes + 512) / 1024;
96 snprintf(buf, size, "%4lld%c%s",
97 (int64_t) bytes,
98 unit[i],
99 i ? "B" : " ");
100}
111 101
112void 102void
113start_progress_meter(char *file, off_t filesize, off_t *counter) 103refresh_progress_meter(void)
114{ 104{
115 if ((curfile = basename(file)) == NULL) 105 char buf[MAX_WINSIZE + 1];
116 curfile = file; 106 time_t now;
107 off_t transferred;
108 double elapsed;
109 int percent;
110 int bytes_left;
111 int cur_speed;
112 int hours, minutes, seconds;
113 int i, len;
114 int file_len;
115
116 transferred = *counter - cur_pos;
117 cur_pos = *counter;
118 now = time(NULL);
119 bytes_left = end_pos - cur_pos;
120
121 if (bytes_left > 0)
122 elapsed = now - last_update;
123 else
124 elapsed = now - start;
117 125
118 totalbytes = filesize; 126 /* calculate speed */
119 statbytes = counter; 127 if (elapsed != 0)
120 (void) gettimeofday(&start, (struct timezone *) 0); 128 cur_speed = (transferred / elapsed);
121 lastupdate = start; 129 else
122 lastsize = 0; 130 cur_speed = 0;
123 131
124 draw_progress_meter(); 132#define AGE_FACTOR 0.9
125 mysignal(SIGALRM, update_progress_meter); 133 if (bytes_per_second != 0) {
126 alarm(PROGRESSTIME); 134 bytes_per_second = (bytes_per_second * AGE_FACTOR) +
127} 135 (cur_speed * (1.0 - AGE_FACTOR));
136 } else
137 bytes_per_second = cur_speed;
138
139 /* filename */
140 buf[0] = '\0';
141 file_len = win_size - 35;
142 if (file_len > 0) {
143 len = snprintf(buf, file_len, "\r%s", file);
144 for (i = len; i < file_len; i++ )
145 buf[i] = ' ';
146 buf[file_len] = '\0';
147 }
128 148
129void 149 /* percent of transfer done */
130stop_progress_meter(void) 150 if (end_pos != 0)
131{ 151 percent = ((float)cur_pos / end_pos) * 100;
132 alarm(0); 152 else
133 draw_progress_meter(); 153 percent = 100;
134 if (foregroundproc() != 0) 154 snprintf(buf + strlen(buf), win_size - strlen(buf),
135 atomicio(vwrite, fileno(stdout), "\n", 1); 155 " %3d%% ", percent);
156
157 /* amount transferred */
158 format_size(buf + strlen(buf), win_size - strlen(buf),
159 cur_pos);
160 strlcat(buf, " ", win_size);
161
162 /* bandwidth usage */
163 format_rate(buf + strlen(buf), win_size - strlen(buf),
164 bytes_per_second);
165 strlcat(buf, "/s ", win_size);
166
167 /* ETA */
168 if (!transferred)
169 stalled += elapsed;
170 else
171 stalled = 0;
172
173 if (stalled >= STALL_TIME)
174 strlcat(buf, "- stalled -", win_size);
175 else if (bytes_per_second == 0 && bytes_left)
176 strlcat(buf, " --:-- ETA", win_size);
177 else {
178 if (bytes_left > 0)
179 seconds = bytes_left / bytes_per_second;
180 else
181 seconds = elapsed;
182
183 hours = seconds / 3600;
184 seconds -= hours * 3600;
185 minutes = seconds / 60;
186 seconds -= minutes * 60;
187
188 if (hours != 0)
189 snprintf(buf + strlen(buf), win_size - strlen(buf),
190 "%d:%02d:%02d", hours, minutes, seconds);
191 else
192 snprintf(buf + strlen(buf), win_size - strlen(buf),
193 " %02d:%02d", minutes, seconds);
194
195 if (bytes_left > 0)
196 strlcat(buf, " ETA", win_size);
197 else
198 strlcat(buf, " ", win_size);
199 }
200
201 atomicio(vwrite, STDOUT_FILENO, buf, win_size);
202 last_update = now;
136} 203}
137 204
138static void 205static void
139update_progress_meter(int ignore) 206update_progress_meter(int ignore)
140{ 207{
141 int save_errno = errno; 208 int save_errno;
209
210 save_errno = errno;
211
212 if (can_output())
213 refresh_progress_meter();
142 214
143 draw_progress_meter();
144 mysignal(SIGALRM, update_progress_meter); 215 mysignal(SIGALRM, update_progress_meter);
145 alarm(PROGRESSTIME); 216 alarm(UPDATE_INTERVAL);
146 errno = save_errno; 217 errno = save_errno;
147} 218}
148 219
149static int 220void
150foregroundproc(void) 221start_progress_meter(char *f, off_t filesize, off_t *stat)
151{
152 static pid_t pgrp = -1;
153 int ctty_pgrp;
154
155 if (pgrp == -1)
156 pgrp = getpgrp();
157
158#ifdef HAVE_TCGETPGRP
159 return ((ctty_pgrp = tcgetpgrp(STDOUT_FILENO)) != -1 &&
160 ctty_pgrp == pgrp);
161#else
162 return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 &&
163 ctty_pgrp == pgrp));
164#endif
165}
166
167static void
168draw_progress_meter(void)
169{ 222{
170 static const char spaces[] = " " 223 struct winsize winsize;
171 " "
172 " "
173 " "
174 " "
175 " ";
176 static const char prefixes[] = " KMGTP";
177 struct timeval now, td, wait;
178 off_t cursize, abbrevsize, bytespersec;
179 double elapsed;
180 int ratio, remaining, i, ai, bi, nspaces;
181 char buf[512];
182
183 if (foregroundproc() == 0)
184 return;
185 224
186 (void) gettimeofday(&now, (struct timezone *) 0); 225 start = last_update = time(NULL);
187 cursize = *statbytes; 226 file = f;
188 if (totalbytes != 0) { 227 end_pos = filesize;
189 ratio = 100.0 * cursize / totalbytes; 228 cur_pos = 0;
190 ratio = MAX(ratio, 0); 229 counter = stat;
191 ratio = MIN(ratio, 100); 230 stalled = 0;
231 bytes_per_second = 0;
232
233 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 &&
234 winsize.ws_col != 0) {
235 if (winsize.ws_col > MAX_WINSIZE)
236 win_size = MAX_WINSIZE;
237 else
238 win_size = winsize.ws_col;
192 } else 239 } else
193 ratio = 100; 240 win_size = DEFAULT_WINSIZE;
194 241 win_size += 1; /* trailing \0 */
195 abbrevsize = cursize;
196 for (ai = 0; abbrevsize >= 10000 && ai < sizeof(prefixes); ai++)
197 abbrevsize >>= 10;
198 242
199 timersub(&now, &lastupdate, &wait); 243 if (can_output())
200 if (cursize > lastsize) { 244 refresh_progress_meter();
201 lastupdate = now;
202 lastsize = cursize;
203 wait.tv_sec = 0;
204 }
205 timersub(&now, &start, &td);
206 elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
207
208 bytespersec = 0;
209 if (cursize > 0) {
210 bytespersec = cursize;
211 if (elapsed > 0.0)
212 bytespersec /= elapsed;
213 }
214 for (bi = 1; bytespersec >= 1024000 && bi < sizeof(prefixes); bi++)
215 bytespersec >>= 10;
216
217 nspaces = MIN(get_tty_width() - 79, sizeof(spaces) - 1);
218
219 snprintf(buf, sizeof(buf),
220 "\r%-45.45s%.*s%3d%% %4lld%c%c %3lld.%01d%cB/s",
221 curfile,
222 nspaces,
223 spaces,
224 ratio,
225 (int64_t)abbrevsize,
226 prefixes[ai],
227 ai == 0 ? ' ' : 'B',
228 (int64_t)(bytespersec / 1024),
229 (int)((bytespersec % 1024) * 10 / 1024),
230 prefixes[bi]
231 );
232
233 if (cursize <= 0 || elapsed <= 0.0 || cursize > totalbytes) {
234 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
235 " --:-- ETA");
236 } else if (wait.tv_sec >= STALLTIME) {
237 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
238 " - stalled -");
239 } else {
240 if (cursize != totalbytes)
241 remaining = (int)(totalbytes / (cursize / elapsed) -
242 elapsed);
243 else
244 remaining = elapsed;
245 245
246 i = remaining / 3600; 246 mysignal(SIGALRM, update_progress_meter);
247 if (i) 247 alarm(UPDATE_INTERVAL);
248 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
249 "%2d:", i);
250 else
251 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
252 " ");
253 i = remaining % 3600;
254 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
255 "%02d:%02d%s", i / 60, i % 60,
256 (cursize != totalbytes) ? " ETA" : " ");
257 }
258 atomicio(vwrite, fileno(stdout), buf, strlen(buf));
259} 248}
260 249
261static int 250void
262get_tty_width(void) 251stop_progress_meter(void)
263{ 252{
264 struct winsize winsize; 253 alarm(0);
265 254
266 if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) 255 if (!can_output())
267 return (winsize.ws_col ? winsize.ws_col : 80); 256 return;
268 else 257
269 return (80); 258 /* Ensure we complete the progress */
259 if (cur_pos != end_pos)
260 refresh_progress_meter();
261
262 atomicio(vwrite, STDOUT_FILENO, "\n", 1);
270} 263}