]>
git.cameronkatri.com Git - bsd-progress.git/blob - progressbar.c
1 /* $NetBSD: progressbar.c,v 1.1 2003/01/21 16:08:08 jhawk Exp $ */
4 * Copyright (c) 1997-2003 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
12 * NASA Ames Research Center.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the NetBSD
25 * Foundation, Inc. and its contributors.
26 * 4. Neither the name of The NetBSD Foundation nor the names of its
27 * contributors may be used to endorse or promote products derived
28 * from this software without specific prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
31 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
32 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
34 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 * POSSIBILITY OF SUCH DAMAGE.
44 * Copyright (c) 1985, 1989, 1993, 1994
45 * The Regents of the University of California. All rights reserved.
47 * Redistribution and use in source and binary forms, with or without
48 * modification, are permitted provided that the following conditions
50 * 1. Redistributions of source code must retain the above copyright
51 * notice, this list of conditions and the following disclaimer.
52 * 2. Redistributions in binary form must reproduce the above copyright
53 * notice, this list of conditions and the following disclaimer in the
54 * documentation and/or other materials provided with the distribution.
55 * 3. All advertising materials mentioning features or use of this software
56 * must display the following acknowledgement:
57 * This product includes software developed by the University of
58 * California, Berkeley and its contributors.
59 * 4. Neither the name of the University nor the names of its contributors
60 * may be used to endorse or promote products derived from this software
61 * without specific prior written permission.
63 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
64 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
65 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
66 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
67 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
68 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
69 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
70 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
71 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
72 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
76 #include <sys/cdefs.h>
78 __RCSID("$NetBSD: progressbar.c,v 1.1 2003/01/21 16:08:08 jhawk Exp $");
82 * FTP User Program -- Misc support routines
84 #include <sys/types.h>
85 #include <sys/param.h>
96 #include "progressbar.h"
98 #if !defined(NO_PROGRESS) && !defined(STANDALONE_PROGRESS)
100 * return non-zero if we're the current foreground process
105 static pid_t pgrp
= -1;
110 return (tcgetpgrp(fileno(ttyout
)) == pgrp
);
112 #endif /* !defined(NO_PROGRESS) && !defined(STANDALONE_PROGRESS) */
116 static void updateprogressmeter(int);
119 * SIGALRM handler to update the progress meter
122 updateprogressmeter(int dummy
)
129 #endif /* NO_PROGRESS */
133 * List of order of magnitude prefixes.
134 * The last is `P', as 2^64 = 16384 Petabytes
136 static const char prefixes
[] = " KMGTP";
139 * Display a transfer progress bar if progress is non-zero.
140 * SIGALRM is hijacked for use by this function.
141 * - Before the transfer, set filesize to size of file (or -1 if unknown),
142 * and call with flag = -1. This starts the once per second timer,
143 * and a call to updateprogressmeter() upon SIGALRM.
144 * - During the transfer, updateprogressmeter will call progressmeter
146 * - After the transfer, call with flag = 1
148 static struct timeval start
;
149 static struct timeval lastupdate
;
151 #define BUFLEFT (sizeof(buf) - len)
154 progressmeter(int flag
)
156 static off_t lastsize
;
158 struct timeval now
, wait
;
161 off_t abbrevsize
, bytespersec
;
163 int ratio
, barlength
, i
, len
, remaining
;
166 * Work variables for progress bar.
168 * XXX: if the format of the progress bar changes
169 * (especially the number of characters in the
170 * `static' portion of it), be sure to update
171 * these appropriately.
173 char buf
[256]; /* workspace for progress bar */
174 #define BAROVERHEAD 43 /* non `*' portion of progress bar */
176 * stars should contain at least
177 * sizeof(buf) - BAROVERHEAD entries
179 static const char stars
[] =
180 "*****************************************************************************"
181 "*****************************************************************************"
182 "*****************************************************************************";
187 (void)gettimeofday(&start
, NULL
);
189 lastsize
= restart_point
;
192 (void)gettimeofday(&now
, NULL
);
193 cursize
= bytes
+ restart_point
;
194 timersub(&now
, &lastupdate
, &wait
);
195 if (cursize
> lastsize
) {
200 #ifndef STANDALONE_PROGRESS
201 if (quit_time
> 0 && wait
.tv_sec
> quit_time
) {
202 len
= snprintf(buf
, sizeof(buf
), "\r\n%s: "
203 "transfer aborted because stalled for %lu sec.\r\n",
204 getprogname(), (unsigned long)wait
.tv_sec
);
205 (void)write(fileno(ttyout
), buf
, len
);
206 (void)xsignal(SIGALRM
, SIG_DFL
);
208 siglongjmp(toplevel
, 1);
210 #endif /* !STANDALONE_PROGRESS */
213 * Always set the handler even if we are not the foreground process.
215 #ifdef STANDALONE_PROGRESS
218 if (quit_time
> 0 || progress
) {
219 #endif /* !STANDALONE_PROGRESS */
221 (void)xsignal_restart(SIGALRM
, updateprogressmeter
, 1);
222 alarmtimer(1); /* set alarm timer for 1 Hz */
223 } else if (flag
== 1) {
224 (void)xsignal(SIGALRM
, SIG_DFL
);
233 #ifndef STANDALONE_PROGRESS
235 * print progress bar only if we are foreground process.
237 if (! foregroundproc())
239 #endif /* !STANDALONE_PROGRESS */
242 len
+= snprintf(buf
+ len
, BUFLEFT
, "\r");
244 ratio
= (int)((double)cursize
* 100.0 / (double)filesize
);
245 ratio
= MAX(ratio
, 0);
246 ratio
= MIN(ratio
, 100);
247 len
+= snprintf(buf
+ len
, BUFLEFT
, "%3d%% ", ratio
);
250 * calculate the length of the `*' bar, ensuring that
251 * the number of stars won't exceed the buffer size
253 barlength
= MIN(sizeof(buf
) - 1, ttywidth
) - BAROVERHEAD
;
255 i
= barlength
* ratio
/ 100;
256 len
+= snprintf(buf
+ len
, BUFLEFT
,
257 "|%.*s%*s|", i
, stars
, barlength
- i
, "");
261 abbrevsize
= cursize
;
262 for (i
= 0; abbrevsize
>= 100000 && i
< sizeof(prefixes
); i
++)
264 len
+= snprintf(buf
+ len
, BUFLEFT
, " " LLFP("5") " %c%c ",
269 timersub(&now
, &start
, &td
);
270 elapsed
= td
.tv_sec
+ (td
.tv_usec
/ 1000000.0);
276 bytespersec
/= elapsed
;
278 for (i
= 1; bytespersec
>= 1024000 && i
< sizeof(prefixes
); i
++)
280 len
+= snprintf(buf
+ len
, BUFLEFT
,
281 " " LLFP("3") ".%02d %cB/s ",
282 (LLT
)(bytespersec
/ 1024),
283 (int)((bytespersec
% 1024) * 100 / 1024),
287 if (bytes
<= 0 || elapsed
<= 0.0 || cursize
> filesize
) {
288 len
+= snprintf(buf
+ len
, BUFLEFT
, " --:-- ETA");
289 } else if (wait
.tv_sec
>= STALLTIME
) {
290 len
+= snprintf(buf
+ len
, BUFLEFT
, " - stalled -");
293 ((filesize
- restart_point
) / (bytes
/ elapsed
) -
295 if (remaining
>= 100 * SECSPERHOUR
)
296 len
+= snprintf(buf
+ len
, BUFLEFT
,
299 i
= remaining
/ SECSPERHOUR
;
301 len
+= snprintf(buf
+ len
, BUFLEFT
,
304 len
+= snprintf(buf
+ len
, BUFLEFT
,
306 i
= remaining
% SECSPERHOUR
;
307 len
+= snprintf(buf
+ len
, BUFLEFT
,
308 "%02d:%02d ETA", i
/ 60, i
% 60);
313 len
+= snprintf(buf
+ len
, BUFLEFT
, "\n");
314 (void)write(fileno(ttyout
), buf
, len
);
316 #endif /* !NO_PROGRESS */
319 #ifndef STANDALONE_PROGRESS
321 * Display transfer statistics.
322 * Requires start to be initialised by progressmeter(-1),
323 * direction to be defined by xfer routines, and filesize and bytes
324 * to be updated by xfer routines
325 * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr
329 ptransfer(int siginfo
)
331 struct timeval now
, td
, wait
;
334 int remaining
, hh
, i
, len
;
336 char buf
[256]; /* Work variable for transfer status. */
338 if (!verbose
&& !progress
&& !siginfo
)
341 (void)gettimeofday(&now
, NULL
);
342 timersub(&now
, &start
, &td
);
343 elapsed
= td
.tv_sec
+ (td
.tv_usec
/ 1000000.0);
348 bytespersec
/= elapsed
;
351 len
+= snprintf(buf
+ len
, BUFLEFT
, LLF
" byte%s %s in ",
352 (LLT
)bytes
, bytes
== 1 ? "" : "s", direction
);
353 remaining
= (int)elapsed
;
354 if (remaining
> SECSPERDAY
) {
357 days
= remaining
/ SECSPERDAY
;
358 remaining
%= SECSPERDAY
;
359 len
+= snprintf(buf
+ len
, BUFLEFT
,
360 "%d day%s ", days
, days
== 1 ? "" : "s");
362 hh
= remaining
/ SECSPERHOUR
;
363 remaining
%= SECSPERHOUR
;
365 len
+= snprintf(buf
+ len
, BUFLEFT
, "%2d:", hh
);
366 len
+= snprintf(buf
+ len
, BUFLEFT
,
367 "%02d:%02d ", remaining
/ 60, remaining
% 60);
369 for (i
= 1; bytespersec
>= 1024000 && i
< sizeof(prefixes
); i
++)
371 len
+= snprintf(buf
+ len
, BUFLEFT
, "(" LLF
".%02d %cB/s)",
372 (LLT
)(bytespersec
/ 1024),
373 (int)((bytespersec
% 1024) * 100 / 1024),
376 if (siginfo
&& bytes
> 0 && elapsed
> 0.0 && filesize
>= 0
377 && bytes
+ restart_point
<= filesize
) {
378 remaining
= (int)((filesize
- restart_point
) /
379 (bytes
/ elapsed
) - elapsed
);
380 hh
= remaining
/ SECSPERHOUR
;
381 remaining
%= SECSPERHOUR
;
382 len
+= snprintf(buf
+ len
, BUFLEFT
, " ETA: ");
384 len
+= snprintf(buf
+ len
, BUFLEFT
, "%2d:", hh
);
385 len
+= snprintf(buf
+ len
, BUFLEFT
, "%02d:%02d",
386 remaining
/ 60, remaining
% 60);
387 timersub(&now
, &lastupdate
, &wait
);
388 if (wait
.tv_sec
>= STALLTIME
)
389 len
+= snprintf(buf
+ len
, BUFLEFT
, " (stalled)");
391 len
+= snprintf(buf
+ len
, BUFLEFT
, "\n");
392 (void)write(siginfo
? STDERR_FILENO
: fileno(ttyout
), buf
, len
);
396 * SIG{INFO,QUIT} handler to print transfer stats if a transfer is in progress
399 psummary(int notused
)
405 write(fileno(ttyout
), "\n", 1);
410 #endif /* !STANDALONE_PROGRESS */
414 * Set the SIGALRM interval timer for wait seconds, 0 to disable.
419 struct itimerval itv
;
421 itv
.it_value
.tv_sec
= wait
;
422 itv
.it_value
.tv_usec
= 0;
423 itv
.it_interval
= itv
.it_value
;
424 setitimer(ITIMER_REAL
, &itv
, NULL
);
429 * Install a POSIX signal handler, allowing the invoker to set whether
430 * the signal should be restartable or not
433 xsignal_restart(int sig
, sigfunc func
, int restartable
)
435 struct sigaction act
, oact
;
436 act
.sa_handler
= func
;
438 sigemptyset(&act
.sa_mask
);
439 #if defined(SA_RESTART) /* 4.4BSD, Posix(?), SVR4 */
440 act
.sa_flags
= restartable
? SA_RESTART
: 0;
441 #elif defined(SA_INTERRUPT) /* SunOS 4.x */
442 act
.sa_flags
= restartable
? 0 : SA_INTERRUPT
;
444 #error "system must have SA_RESTART or SA_INTERRUPT"
446 if (sigaction(sig
, &act
, &oact
) < 0)
448 return (oact
.sa_handler
);
452 * Install a signal handler with the `restartable' flag set dependent upon
453 * which signal is being set. (This is a wrapper to xsignal_restart())
456 xsignal(int sig
, sigfunc func
)
461 * Some signals print output or change the state of the process.
462 * There should be restartable, so that reads and writes are
463 * not affected. Some signals should cause program flow to change;
464 * these signals should not be restartable, so that the system call
465 * will return with EINTR, and the program will go do something
466 * different. If the signal handler calls longjmp() or siglongjmp(),
467 * it doesn't matter if it's restartable.
489 * This is unpleasant, but I don't know what would be better.
490 * Right now, this "can't happen"
492 errx(1, "xsignal_restart called with signal %d", sig
);
495 return(xsignal_restart(sig
, func
, restartable
));