]>
git.cameronkatri.com Git - bsd-progress.git/blob - progressbar.c
1 /* $NetBSD: progressbar.c,v 1.3 2003/02/28 09:53:49 lukem 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.
43 #include <sys/cdefs.h>
45 __RCSID("$NetBSD: progressbar.c,v 1.3 2003/02/28 09:53:49 lukem Exp $");
49 * FTP User Program -- Misc support routines
51 #include <sys/types.h>
52 #include <sys/param.h>
63 #include "progressbar.h"
65 #if !defined(NO_PROGRESS)
67 * return non-zero if we're the current foreground process
72 static pid_t pgrp
= -1;
77 return (tcgetpgrp(fileno(ttyout
)) == pgrp
);
79 #endif /* !defined(NO_PROGRESS) */
83 static void updateprogressmeter(int);
86 * SIGALRM handler to update the progress meter
89 updateprogressmeter(int dummy
)
96 #endif /* NO_PROGRESS */
100 * List of order of magnitude prefixes.
101 * The last is `P', as 2^64 = 16384 Petabytes
103 static const char prefixes
[] = " KMGTP";
106 * Display a transfer progress bar if progress is non-zero.
107 * SIGALRM is hijacked for use by this function.
108 * - Before the transfer, set filesize to size of file (or -1 if unknown),
109 * and call with flag = -1. This starts the once per second timer,
110 * and a call to updateprogressmeter() upon SIGALRM.
111 * - During the transfer, updateprogressmeter will call progressmeter
113 * - After the transfer, call with flag = 1
115 static struct timeval start
;
116 static struct timeval lastupdate
;
118 #define BUFLEFT (sizeof(buf) - len)
121 progressmeter(int flag
)
123 static off_t lastsize
;
125 struct timeval now
, wait
;
128 off_t abbrevsize
, bytespersec
;
130 int ratio
, barlength
, i
, len
, remaining
;
133 * Work variables for progress bar.
135 * XXX: if the format of the progress bar changes
136 * (especially the number of characters in the
137 * `static' portion of it), be sure to update
138 * these appropriately.
140 char buf
[256]; /* workspace for progress bar */
141 #define BAROVERHEAD 43 /* non `*' portion of progress bar */
143 * stars should contain at least
144 * sizeof(buf) - BAROVERHEAD entries
146 static const char stars
[] =
147 "*****************************************************************************"
148 "*****************************************************************************"
149 "*****************************************************************************";
154 (void)gettimeofday(&start
, NULL
);
156 lastsize
= restart_point
;
159 (void)gettimeofday(&now
, NULL
);
160 cursize
= bytes
+ restart_point
;
161 timersub(&now
, &lastupdate
, &wait
);
162 if (cursize
> lastsize
) {
167 #ifndef STANDALONE_PROGRESS
168 if (quit_time
> 0 && wait
.tv_sec
> quit_time
) {
169 len
= snprintf(buf
, sizeof(buf
), "\r\n%s: "
170 "transfer aborted because stalled for %lu sec.\r\n",
171 getprogname(), (unsigned long)wait
.tv_sec
);
172 (void)write(fileno(ttyout
), buf
, len
);
173 (void)xsignal(SIGALRM
, SIG_DFL
);
175 siglongjmp(toplevel
, 1);
177 #endif /* !STANDALONE_PROGRESS */
180 * Always set the handler even if we are not the foreground process.
182 #ifdef STANDALONE_PROGRESS
185 if (quit_time
> 0 || progress
) {
186 #endif /* !STANDALONE_PROGRESS */
188 (void)xsignal_restart(SIGALRM
, updateprogressmeter
, 1);
189 alarmtimer(1); /* set alarm timer for 1 Hz */
190 } else if (flag
== 1) {
191 (void)xsignal(SIGALRM
, SIG_DFL
);
201 * print progress bar only if we are foreground process.
203 if (! foregroundproc())
206 len
+= snprintf(buf
+ len
, BUFLEFT
, "\r");
208 ratio
= (int)((double)cursize
* 100.0 / (double)filesize
);
209 ratio
= MAX(ratio
, 0);
210 ratio
= MIN(ratio
, 100);
211 len
+= snprintf(buf
+ len
, BUFLEFT
, "%3d%% ", ratio
);
214 * calculate the length of the `*' bar, ensuring that
215 * the number of stars won't exceed the buffer size
217 barlength
= MIN(sizeof(buf
) - 1, ttywidth
) - BAROVERHEAD
;
219 i
= barlength
* ratio
/ 100;
220 len
+= snprintf(buf
+ len
, BUFLEFT
,
221 "|%.*s%*s|", i
, stars
, barlength
- i
, "");
225 abbrevsize
= cursize
;
226 for (i
= 0; abbrevsize
>= 100000 && i
< sizeof(prefixes
); i
++)
228 len
+= snprintf(buf
+ len
, BUFLEFT
, " " LLFP("5") " %c%c ",
233 timersub(&now
, &start
, &td
);
234 elapsed
= td
.tv_sec
+ (td
.tv_usec
/ 1000000.0);
240 bytespersec
/= elapsed
;
242 for (i
= 1; bytespersec
>= 1024000 && i
< sizeof(prefixes
); i
++)
244 len
+= snprintf(buf
+ len
, BUFLEFT
,
245 " " LLFP("3") ".%02d %cB/s ",
246 (LLT
)(bytespersec
/ 1024),
247 (int)((bytespersec
% 1024) * 100 / 1024),
251 if (bytes
<= 0 || elapsed
<= 0.0 || cursize
> filesize
) {
252 len
+= snprintf(buf
+ len
, BUFLEFT
, " --:-- ETA");
253 } else if (wait
.tv_sec
>= STALLTIME
) {
254 len
+= snprintf(buf
+ len
, BUFLEFT
, " - stalled -");
257 ((filesize
- restart_point
) / (bytes
/ elapsed
) -
259 if (remaining
>= 100 * SECSPERHOUR
)
260 len
+= snprintf(buf
+ len
, BUFLEFT
,
263 i
= remaining
/ SECSPERHOUR
;
265 len
+= snprintf(buf
+ len
, BUFLEFT
,
268 len
+= snprintf(buf
+ len
, BUFLEFT
,
270 i
= remaining
% SECSPERHOUR
;
271 len
+= snprintf(buf
+ len
, BUFLEFT
,
272 "%02d:%02d ETA", i
/ 60, i
% 60);
277 len
+= snprintf(buf
+ len
, BUFLEFT
, "\n");
278 (void)write(fileno(ttyout
), buf
, len
);
280 #endif /* !NO_PROGRESS */
283 #ifndef STANDALONE_PROGRESS
285 * Display transfer statistics.
286 * Requires start to be initialised by progressmeter(-1),
287 * direction to be defined by xfer routines, and filesize and bytes
288 * to be updated by xfer routines
289 * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr
293 ptransfer(int siginfo
)
295 struct timeval now
, td
, wait
;
298 int remaining
, hh
, i
, len
;
300 char buf
[256]; /* Work variable for transfer status. */
302 if (!verbose
&& !progress
&& !siginfo
)
305 (void)gettimeofday(&now
, NULL
);
306 timersub(&now
, &start
, &td
);
307 elapsed
= td
.tv_sec
+ (td
.tv_usec
/ 1000000.0);
312 bytespersec
/= elapsed
;
315 len
+= snprintf(buf
+ len
, BUFLEFT
, LLF
" byte%s %s in ",
316 (LLT
)bytes
, bytes
== 1 ? "" : "s", direction
);
317 remaining
= (int)elapsed
;
318 if (remaining
> SECSPERDAY
) {
321 days
= remaining
/ SECSPERDAY
;
322 remaining
%= SECSPERDAY
;
323 len
+= snprintf(buf
+ len
, BUFLEFT
,
324 "%d day%s ", days
, days
== 1 ? "" : "s");
326 hh
= remaining
/ SECSPERHOUR
;
327 remaining
%= SECSPERHOUR
;
329 len
+= snprintf(buf
+ len
, BUFLEFT
, "%2d:", hh
);
330 len
+= snprintf(buf
+ len
, BUFLEFT
,
331 "%02d:%02d ", remaining
/ 60, remaining
% 60);
333 for (i
= 1; bytespersec
>= 1024000 && i
< sizeof(prefixes
); i
++)
335 len
+= snprintf(buf
+ len
, BUFLEFT
, "(" LLF
".%02d %cB/s)",
336 (LLT
)(bytespersec
/ 1024),
337 (int)((bytespersec
% 1024) * 100 / 1024),
340 if (siginfo
&& bytes
> 0 && elapsed
> 0.0 && filesize
>= 0
341 && bytes
+ restart_point
<= filesize
) {
342 remaining
= (int)((filesize
- restart_point
) /
343 (bytes
/ elapsed
) - elapsed
);
344 hh
= remaining
/ SECSPERHOUR
;
345 remaining
%= SECSPERHOUR
;
346 len
+= snprintf(buf
+ len
, BUFLEFT
, " ETA: ");
348 len
+= snprintf(buf
+ len
, BUFLEFT
, "%2d:", hh
);
349 len
+= snprintf(buf
+ len
, BUFLEFT
, "%02d:%02d",
350 remaining
/ 60, remaining
% 60);
351 timersub(&now
, &lastupdate
, &wait
);
352 if (wait
.tv_sec
>= STALLTIME
)
353 len
+= snprintf(buf
+ len
, BUFLEFT
, " (stalled)");
355 len
+= snprintf(buf
+ len
, BUFLEFT
, "\n");
356 (void)write(siginfo
? STDERR_FILENO
: fileno(ttyout
), buf
, len
);
360 * SIG{INFO,QUIT} handler to print transfer stats if a transfer is in progress
363 psummary(int notused
)
369 write(fileno(ttyout
), "\n", 1);
374 #endif /* !STANDALONE_PROGRESS */
378 * Set the SIGALRM interval timer for wait seconds, 0 to disable.
383 struct itimerval itv
;
385 itv
.it_value
.tv_sec
= wait
;
386 itv
.it_value
.tv_usec
= 0;
387 itv
.it_interval
= itv
.it_value
;
388 setitimer(ITIMER_REAL
, &itv
, NULL
);
393 * Install a POSIX signal handler, allowing the invoker to set whether
394 * the signal should be restartable or not
397 xsignal_restart(int sig
, sigfunc func
, int restartable
)
399 struct sigaction act
, oact
;
400 act
.sa_handler
= func
;
402 sigemptyset(&act
.sa_mask
);
403 #if defined(SA_RESTART) /* 4.4BSD, Posix(?), SVR4 */
404 act
.sa_flags
= restartable
? SA_RESTART
: 0;
405 #elif defined(SA_INTERRUPT) /* SunOS 4.x */
406 act
.sa_flags
= restartable
? 0 : SA_INTERRUPT
;
408 #error "system must have SA_RESTART or SA_INTERRUPT"
410 if (sigaction(sig
, &act
, &oact
) < 0)
412 return (oact
.sa_handler
);
416 * Install a signal handler with the `restartable' flag set dependent upon
417 * which signal is being set. (This is a wrapper to xsignal_restart())
420 xsignal(int sig
, sigfunc func
)
425 * Some signals print output or change the state of the process.
426 * There should be restartable, so that reads and writes are
427 * not affected. Some signals should cause program flow to change;
428 * these signals should not be restartable, so that the system call
429 * will return with EINTR, and the program will go do something
430 * different. If the signal handler calls longjmp() or siglongjmp(),
431 * it doesn't matter if it's restartable.
453 * This is unpleasant, but I don't know what would be better.
454 * Right now, this "can't happen"
456 errx(1, "xsignal_restart called with signal %d", sig
);
459 return(xsignal_restart(sig
, func
, restartable
));