]>
git.cameronkatri.com Git - bsd-progress.git/blob - progressbar.c
1 /* $NetBSD: progressbar.c,v 1.24 2021/01/06 04:43:14 lukem Exp $ */
4 * Copyright (c) 1997-2021 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: progressbar.c,v 1.24 2021/01/06 04:43:14 lukem Exp $");
38 * FTP User Program -- Misc support routines
40 #include <sys/param.h>
41 #include <sys/types.h>
54 #include "progressbar.h"
56 #if !defined(NO_PROGRESS)
58 * return non-zero if we're the current foreground process
63 static pid_t pgrp
= -1;
68 return (tcgetpgrp(fileno(ttyout
)) == pgrp
);
70 #endif /* !defined(NO_PROGRESS) */
73 static void updateprogressmeter(int);
76 * SIGALRM handler to update the progress meter
79 updateprogressmeter(int dummy
)
88 * List of order of magnitude suffixes, per IEC 60027-2.
90 #if !defined(NO_PROGRESS) || !defined(STANDALONE_PROGRESS)
91 static const char * const suffixes
[] = {
93 "KiB", /* 2^10 Kibibyte */
94 "MiB", /* 2^20 Mebibyte */
95 "GiB", /* 2^30 Gibibyte */
96 "TiB", /* 2^40 Tebibyte */
97 "PiB", /* 2^50 Pebibyte */
98 "EiB", /* 2^60 Exbibyte */
100 /* The following are not necessary for signed 64-bit off_t */
101 "ZiB", /* 2^70 Zebibyte */
102 "YiB", /* 2^80 Yobibyte */
105 #define NSUFFIXES (int)(sizeof(suffixes) / sizeof(suffixes[0]))
109 * Display a transfer progress bar if progress is non-zero.
110 * SIGALRM is hijacked for use by this function.
111 * - Before the transfer, set filesize to size of file (or -1 if unknown),
112 * and call with flag = -1. This starts the once per second timer,
113 * and a call to updateprogressmeter() upon SIGALRM.
114 * - During the transfer, updateprogressmeter will call progressmeter
116 * - After the transfer, call with flag = 1
118 static struct timeval start
;
119 static struct timeval lastupdate
;
121 #define BUFLEFT (sizeof(buf) - len)
124 progressmeter(int flag
)
126 static off_t lastsize
;
128 struct timeval now
, wait
;
131 off_t abbrevsize
, bytespersec
;
133 int ratio
, i
, remaining
, barlength
;
136 * Work variables for progress bar.
138 * XXX: if the format of the progress bar changes
139 * (especially the number of characters in the
140 * `static' portion of it), be sure to update
141 * these appropriately.
144 #if !defined(NO_PROGRESS) || !defined(STANDALONE_PROGRESS)
146 char buf
[256]; /* workspace for progress bar */
149 #define BAROVERHEAD 45 /* non `*' portion of progress bar */
151 * stars should contain at least
152 * sizeof(buf) - BAROVERHEAD entries
154 static const char stars
[] =
155 "*****************************************************************************"
156 "*****************************************************************************"
157 "*****************************************************************************";
162 (void)gettimeofday(&start
, NULL
);
164 lastsize
= restart_point
;
167 (void)gettimeofday(&now
, NULL
);
168 cursize
= bytes
+ restart_point
;
169 timersub(&now
, &lastupdate
, &wait
);
170 if (cursize
> lastsize
) {
175 #ifndef STANDALONE_PROGRESS
176 if (quit_time
> 0 && wait
.tv_sec
> quit_time
) {
177 len
= snprintf(buf
, sizeof(buf
), "\r\n%s: "
178 "transfer aborted because stalled for %lu sec.\r\n",
179 getprogname(), (unsigned long)wait
.tv_sec
);
180 (void)write(fileno(ttyout
), buf
, len
);
182 (void)xsignal(SIGALRM
, SIG_DFL
);
183 siglongjmp(toplevel
, 1);
185 #endif /* !STANDALONE_PROGRESS */
188 * Always set the handler even if we are not the foreground process.
190 #ifdef STANDALONE_PROGRESS
193 if (quit_time
> 0 || progress
) {
194 #endif /* !STANDALONE_PROGRESS */
196 (void)xsignal(SIGALRM
, updateprogressmeter
);
197 alarmtimer(1); /* set alarm timer for 1 Hz */
198 } else if (flag
== 1) {
200 (void)xsignal(SIGALRM
, SIG_DFL
);
209 * print progress bar only if we are foreground process.
211 if (! foregroundproc())
214 len
+= snprintf(buf
+ len
, BUFLEFT
, "\r");
216 len
+= snprintf(buf
+ len
, BUFLEFT
, "%s", prefix
);
218 ratio
= (int)((double)cursize
* 100.0 / (double)filesize
);
219 ratio
= MAX(ratio
, 0);
220 ratio
= MIN(ratio
, 100);
221 len
+= snprintf(buf
+ len
, BUFLEFT
, "%3d%% ", ratio
);
224 * calculate the length of the `*' bar, ensuring that
225 * the number of stars won't exceed the buffer size
227 barlength
= MIN((int)(sizeof(buf
) - 1), ttywidth
) - BAROVERHEAD
;
229 barlength
-= (int)strlen(prefix
);
231 i
= barlength
* ratio
/ 100;
232 len
+= snprintf(buf
+ len
, BUFLEFT
,
233 "|%.*s%*s|", i
, stars
, (int)(barlength
- i
), "");
237 abbrevsize
= cursize
;
238 for (i
= 0; abbrevsize
>= 100000 && i
< NSUFFIXES
; i
++)
242 len
+= snprintf(buf
+ len
, BUFLEFT
, " " LLFP("5") " %-3s ",
246 timersub(&now
, &start
, &td
);
247 elapsed
= td
.tv_sec
+ (td
.tv_usec
/ 1000000.0);
253 bytespersec
/= elapsed
;
255 for (i
= 1; bytespersec
>= 1024000 && i
< NSUFFIXES
; i
++)
257 len
+= snprintf(buf
+ len
, BUFLEFT
,
258 " " LLFP("3") ".%02d %.2sB/s ",
259 (LLT
)(bytespersec
/ 1024),
260 (int)((bytespersec
% 1024) * 100 / 1024),
264 if (bytes
<= 0 || elapsed
<= 0.0 || cursize
> filesize
) {
265 len
+= snprintf(buf
+ len
, BUFLEFT
, " --:-- ETA");
266 } else if (wait
.tv_sec
>= STALLTIME
) {
267 len
+= snprintf(buf
+ len
, BUFLEFT
, " - stalled -");
270 ((filesize
- restart_point
) / (bytes
/ elapsed
) -
272 if (remaining
>= 100 * SECSPERHOUR
)
273 len
+= snprintf(buf
+ len
, BUFLEFT
,
276 i
= remaining
/ SECSPERHOUR
;
278 len
+= snprintf(buf
+ len
, BUFLEFT
,
281 len
+= snprintf(buf
+ len
, BUFLEFT
,
283 i
= remaining
% SECSPERHOUR
;
284 len
+= snprintf(buf
+ len
, BUFLEFT
,
285 "%02d:%02d ETA", i
/ 60, i
% 60);
290 len
+= snprintf(buf
+ len
, BUFLEFT
, "\n");
291 (void)write(fileno(ttyout
), buf
, len
);
293 #endif /* !NO_PROGRESS */
296 #ifndef STANDALONE_PROGRESS
298 * Display transfer statistics.
299 * Requires start to be initialised by progressmeter(-1),
300 * direction to be defined by xfer routines, and filesize and bytes
301 * to be updated by xfer routines
302 * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr
306 ptransfer(int siginfo
)
308 struct timeval now
, td
, wait
;
311 int remaining
, hh
, i
;
314 char buf
[256]; /* Work variable for transfer status. */
316 if (!verbose
&& !progress
&& !siginfo
)
319 (void)gettimeofday(&now
, NULL
);
320 timersub(&now
, &start
, &td
);
321 elapsed
= td
.tv_sec
+ (td
.tv_usec
/ 1000000.0);
326 bytespersec
/= elapsed
;
329 len
+= snprintf(buf
+ len
, BUFLEFT
, LLF
" byte%s %s in ",
330 (LLT
)bytes
, bytes
== 1 ? "" : "s", direction
);
331 remaining
= (int)elapsed
;
332 if (remaining
> SECSPERDAY
) {
335 days
= remaining
/ SECSPERDAY
;
336 remaining
%= SECSPERDAY
;
337 len
+= snprintf(buf
+ len
, BUFLEFT
,
338 "%d day%s ", days
, days
== 1 ? "" : "s");
340 hh
= remaining
/ SECSPERHOUR
;
341 remaining
%= SECSPERHOUR
;
343 len
+= snprintf(buf
+ len
, BUFLEFT
, "%2d:", hh
);
344 len
+= snprintf(buf
+ len
, BUFLEFT
,
345 "%02d:%02d ", remaining
/ 60, remaining
% 60);
347 for (i
= 1; bytespersec
>= 1024000 && i
< NSUFFIXES
; i
++)
351 len
+= snprintf(buf
+ len
, BUFLEFT
, "(" LLF
".%02d %.2sB/s)",
352 (LLT
)(bytespersec
/ 1024),
353 (int)((bytespersec
% 1024) * 100 / 1024),
356 if (siginfo
&& bytes
> 0 && elapsed
> 0.0 && filesize
>= 0
357 && bytes
+ restart_point
<= filesize
) {
358 remaining
= (int)((filesize
- restart_point
) /
359 (bytes
/ elapsed
) - elapsed
);
360 hh
= remaining
/ SECSPERHOUR
;
361 remaining
%= SECSPERHOUR
;
362 len
+= snprintf(buf
+ len
, BUFLEFT
, " ETA: ");
364 len
+= snprintf(buf
+ len
, BUFLEFT
, "%2d:", hh
);
365 len
+= snprintf(buf
+ len
, BUFLEFT
, "%02d:%02d",
366 remaining
/ 60, remaining
% 60);
367 timersub(&now
, &lastupdate
, &wait
);
368 if (wait
.tv_sec
>= STALLTIME
)
369 len
+= snprintf(buf
+ len
, BUFLEFT
, " (stalled)");
371 len
+= snprintf(buf
+ len
, BUFLEFT
, "\n");
372 (void)write(siginfo
? STDERR_FILENO
: fileno(ttyout
), buf
, len
);
376 * SIG{INFO,QUIT} handler to print transfer stats if a transfer is in progress
379 psummary(int notused
)
385 write(fileno(ttyout
), "\n", 1);
390 #endif /* !STANDALONE_PROGRESS */
394 * Set the SIGALRM interval timer for wait seconds, 0 to disable.
399 struct itimerval itv
;
401 itv
.it_value
.tv_sec
= wait
;
402 itv
.it_value
.tv_usec
= 0;
403 itv
.it_interval
= itv
.it_value
;
404 setitimer(ITIMER_REAL
, &itv
, NULL
);
408 * Install a non-restartable POSIX signal handler.
411 xsignal(int sig
, sigfunc func
)
413 struct sigaction act
, oact
;
414 act
.sa_handler
= func
;
416 sigemptyset(&act
.sa_mask
);
418 #if defined(SA_INTERRUPT) /* SunOS 4.x */
419 act
.sa_flags
= SA_INTERRUPT
;
421 if (sigaction(sig
, &act
, &oact
) < 0)
423 return (oact
.sa_handler
);