xref: /freebsd/contrib/tnftp/src/progressbar.c (revision 935205e2307611615ed5a7fe0a32b225ffd8c19c)
1*cc361f65SGavin Atkinson /*	$NetBSD: progressbar.c,v 1.14 2009/05/20 12:53:47 lukem Exp $	*/
2*cc361f65SGavin Atkinson /*	from	NetBSD: progressbar.c,v 1.21 2009/04/12 10:18:52 lukem Exp	*/
3f982db4aSGavin Atkinson 
4f982db4aSGavin Atkinson /*-
5*cc361f65SGavin Atkinson  * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
6f982db4aSGavin Atkinson  * All rights reserved.
7f982db4aSGavin Atkinson  *
8f982db4aSGavin Atkinson  * This code is derived from software contributed to The NetBSD Foundation
9f982db4aSGavin Atkinson  * by Luke Mewburn.
10f982db4aSGavin Atkinson  *
11f982db4aSGavin Atkinson  * Redistribution and use in source and binary forms, with or without
12f982db4aSGavin Atkinson  * modification, are permitted provided that the following conditions
13f982db4aSGavin Atkinson  * are met:
14f982db4aSGavin Atkinson  * 1. Redistributions of source code must retain the above copyright
15f982db4aSGavin Atkinson  *    notice, this list of conditions and the following disclaimer.
16f982db4aSGavin Atkinson  * 2. Redistributions in binary form must reproduce the above copyright
17f982db4aSGavin Atkinson  *    notice, this list of conditions and the following disclaimer in the
18f982db4aSGavin Atkinson  *    documentation and/or other materials provided with the distribution.
19f982db4aSGavin Atkinson  *
20f982db4aSGavin Atkinson  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21f982db4aSGavin Atkinson  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22f982db4aSGavin Atkinson  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23f982db4aSGavin Atkinson  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24f982db4aSGavin Atkinson  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25f982db4aSGavin Atkinson  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26f982db4aSGavin Atkinson  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27f982db4aSGavin Atkinson  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28f982db4aSGavin Atkinson  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29f982db4aSGavin Atkinson  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30f982db4aSGavin Atkinson  * POSSIBILITY OF SUCH DAMAGE.
31f982db4aSGavin Atkinson  */
32f982db4aSGavin Atkinson 
33*cc361f65SGavin Atkinson #include "tnftp.h"
34*cc361f65SGavin Atkinson 
35*cc361f65SGavin Atkinson #if 0	/* tnftp */
36*cc361f65SGavin Atkinson 
37f982db4aSGavin Atkinson #include <sys/cdefs.h>
38f982db4aSGavin Atkinson #ifndef lint
39*cc361f65SGavin Atkinson __RCSID(" NetBSD: progressbar.c,v 1.21 2009/04/12 10:18:52 lukem Exp  ");
40f982db4aSGavin Atkinson #endif /* not lint */
41f982db4aSGavin Atkinson 
42f982db4aSGavin Atkinson /*
43f982db4aSGavin Atkinson  * FTP User Program -- Misc support routines
44f982db4aSGavin Atkinson  */
45f982db4aSGavin Atkinson #include <sys/types.h>
46f982db4aSGavin Atkinson #include <sys/param.h>
47f982db4aSGavin Atkinson 
48f982db4aSGavin Atkinson #include <err.h>
49f982db4aSGavin Atkinson #include <errno.h>
50f982db4aSGavin Atkinson #include <signal.h>
51f982db4aSGavin Atkinson #include <stdio.h>
52f982db4aSGavin Atkinson #include <stdlib.h>
53f982db4aSGavin Atkinson #include <string.h>
54f982db4aSGavin Atkinson #include <time.h>
55*cc361f65SGavin Atkinson #include <tzfile.h>
56f982db4aSGavin Atkinson #include <unistd.h>
57f982db4aSGavin Atkinson 
58*cc361f65SGavin Atkinson #endif	/* tnftp */
59f982db4aSGavin Atkinson 
60*cc361f65SGavin Atkinson #include "progressbar.h"
61f982db4aSGavin Atkinson 
62f982db4aSGavin Atkinson #if !defined(NO_PROGRESS)
63f982db4aSGavin Atkinson /*
64f982db4aSGavin Atkinson  * return non-zero if we're the current foreground process
65f982db4aSGavin Atkinson  */
66f982db4aSGavin Atkinson int
foregroundproc(void)67f982db4aSGavin Atkinson foregroundproc(void)
68f982db4aSGavin Atkinson {
69f982db4aSGavin Atkinson 	static pid_t pgrp = -1;
70f982db4aSGavin Atkinson 
71f982db4aSGavin Atkinson 	if (pgrp == -1)
72*cc361f65SGavin Atkinson #if GETPGRP_VOID
73f982db4aSGavin Atkinson 		pgrp = getpgrp();
74*cc361f65SGavin Atkinson #else /* ! GETPGRP_VOID */
75*cc361f65SGavin Atkinson 		pgrp = getpgrp(0);
76*cc361f65SGavin Atkinson #endif /* ! GETPGRP_VOID */
77f982db4aSGavin Atkinson 
78f982db4aSGavin Atkinson 	return (tcgetpgrp(fileno(ttyout)) == pgrp);
79f982db4aSGavin Atkinson }
80f982db4aSGavin Atkinson #endif	/* !defined(NO_PROGRESS) */
81f982db4aSGavin Atkinson 
82f982db4aSGavin Atkinson 
83f982db4aSGavin Atkinson static void updateprogressmeter(int);
84f982db4aSGavin Atkinson 
85f982db4aSGavin Atkinson /*
86f982db4aSGavin Atkinson  * SIGALRM handler to update the progress meter
87f982db4aSGavin Atkinson  */
88f982db4aSGavin Atkinson static void
updateprogressmeter(int dummy)89f982db4aSGavin Atkinson updateprogressmeter(int dummy)
90f982db4aSGavin Atkinson {
91f982db4aSGavin Atkinson 	int oerrno = errno;
92f982db4aSGavin Atkinson 
93f982db4aSGavin Atkinson 	progressmeter(0);
94f982db4aSGavin Atkinson 	errno = oerrno;
95f982db4aSGavin Atkinson }
96f982db4aSGavin Atkinson 
97f982db4aSGavin Atkinson /*
98*cc361f65SGavin Atkinson  * List of order of magnitude suffixes, per IEC 60027-2.
99f982db4aSGavin Atkinson  */
100*cc361f65SGavin Atkinson static const char * const suffixes[] = {
101*cc361f65SGavin Atkinson 	"",	/* 2^0  (byte) */
102*cc361f65SGavin Atkinson 	"KiB",	/* 2^10 Kibibyte */
103*cc361f65SGavin Atkinson 	"MiB",	/* 2^20 Mebibyte */
104*cc361f65SGavin Atkinson 	"GiB",	/* 2^30 Gibibyte */
105*cc361f65SGavin Atkinson 	"TiB",	/* 2^40 Tebibyte */
106*cc361f65SGavin Atkinson 	"PiB",	/* 2^50 Pebibyte */
107*cc361f65SGavin Atkinson 	"EiB",	/* 2^60 Exbibyte */
108*cc361f65SGavin Atkinson #if 0
109*cc361f65SGavin Atkinson 		/* The following are not necessary for signed 64-bit off_t */
110*cc361f65SGavin Atkinson 	"ZiB",	/* 2^70 Zebibyte */
111*cc361f65SGavin Atkinson 	"YiB",	/* 2^80 Yobibyte */
112*cc361f65SGavin Atkinson #endif
113*cc361f65SGavin Atkinson };
114*cc361f65SGavin Atkinson #define NSUFFIXES	(int)(sizeof(suffixes) / sizeof(suffixes[0]))
115f982db4aSGavin Atkinson 
116f982db4aSGavin Atkinson /*
117f982db4aSGavin Atkinson  * Display a transfer progress bar if progress is non-zero.
118f982db4aSGavin Atkinson  * SIGALRM is hijacked for use by this function.
119f982db4aSGavin Atkinson  * - Before the transfer, set filesize to size of file (or -1 if unknown),
120f982db4aSGavin Atkinson  *   and call with flag = -1. This starts the once per second timer,
121f982db4aSGavin Atkinson  *   and a call to updateprogressmeter() upon SIGALRM.
122f982db4aSGavin Atkinson  * - During the transfer, updateprogressmeter will call progressmeter
123f982db4aSGavin Atkinson  *   with flag = 0
124f982db4aSGavin Atkinson  * - After the transfer, call with flag = 1
125f982db4aSGavin Atkinson  */
126f982db4aSGavin Atkinson static struct timeval start;
127f982db4aSGavin Atkinson static struct timeval lastupdate;
128f982db4aSGavin Atkinson 
129f982db4aSGavin Atkinson #define	BUFLEFT	(sizeof(buf) - len)
130f982db4aSGavin Atkinson 
131f982db4aSGavin Atkinson void
progressmeter(int flag)132f982db4aSGavin Atkinson progressmeter(int flag)
133f982db4aSGavin Atkinson {
134f982db4aSGavin Atkinson 	static off_t lastsize;
135f982db4aSGavin Atkinson 	off_t cursize;
136f982db4aSGavin Atkinson 	struct timeval now, wait;
137f982db4aSGavin Atkinson #ifndef NO_PROGRESS
138f982db4aSGavin Atkinson 	struct timeval td;
139f982db4aSGavin Atkinson 	off_t abbrevsize, bytespersec;
140f982db4aSGavin Atkinson 	double elapsed;
141*cc361f65SGavin Atkinson 	int ratio, i, remaining, barlength;
142f982db4aSGavin Atkinson 
143f982db4aSGavin Atkinson 			/*
144f982db4aSGavin Atkinson 			 * Work variables for progress bar.
145f982db4aSGavin Atkinson 			 *
146f982db4aSGavin Atkinson 			 * XXX:	if the format of the progress bar changes
147f982db4aSGavin Atkinson 			 *	(especially the number of characters in the
148f982db4aSGavin Atkinson 			 *	`static' portion of it), be sure to update
149f982db4aSGavin Atkinson 			 *	these appropriately.
150f982db4aSGavin Atkinson 			 */
151f982db4aSGavin Atkinson #endif
152*cc361f65SGavin Atkinson 	size_t		len;
153f982db4aSGavin Atkinson 	char		buf[256];	/* workspace for progress bar */
154f982db4aSGavin Atkinson #ifndef NO_PROGRESS
155*cc361f65SGavin Atkinson #define	BAROVERHEAD	45		/* non `*' portion of progress bar */
156f982db4aSGavin Atkinson 					/*
157f982db4aSGavin Atkinson 					 * stars should contain at least
158f982db4aSGavin Atkinson 					 * sizeof(buf) - BAROVERHEAD entries
159f982db4aSGavin Atkinson 					 */
160f982db4aSGavin Atkinson 	static const char	stars[] =
161f982db4aSGavin Atkinson "*****************************************************************************"
162f982db4aSGavin Atkinson "*****************************************************************************"
163f982db4aSGavin Atkinson "*****************************************************************************";
164f982db4aSGavin Atkinson 
165f982db4aSGavin Atkinson #endif
166f982db4aSGavin Atkinson 
167f982db4aSGavin Atkinson 	if (flag == -1) {
168f982db4aSGavin Atkinson 		(void)gettimeofday(&start, NULL);
169f982db4aSGavin Atkinson 		lastupdate = start;
170f982db4aSGavin Atkinson 		lastsize = restart_point;
171f982db4aSGavin Atkinson 	}
172f982db4aSGavin Atkinson 
173f982db4aSGavin Atkinson 	(void)gettimeofday(&now, NULL);
174f982db4aSGavin Atkinson 	cursize = bytes + restart_point;
175f982db4aSGavin Atkinson 	timersub(&now, &lastupdate, &wait);
176f982db4aSGavin Atkinson 	if (cursize > lastsize) {
177f982db4aSGavin Atkinson 		lastupdate = now;
178f982db4aSGavin Atkinson 		lastsize = cursize;
179f982db4aSGavin Atkinson 		wait.tv_sec = 0;
180f982db4aSGavin Atkinson 	} else {
181f982db4aSGavin Atkinson #ifndef STANDALONE_PROGRESS
182f982db4aSGavin Atkinson 		if (quit_time > 0 && wait.tv_sec > quit_time) {
183f982db4aSGavin Atkinson 			len = snprintf(buf, sizeof(buf), "\r\n%s: "
184f982db4aSGavin Atkinson 			    "transfer aborted because stalled for %lu sec.\r\n",
185f982db4aSGavin Atkinson 			    getprogname(), (unsigned long)wait.tv_sec);
186f982db4aSGavin Atkinson 			(void)write(fileno(ttyout), buf, len);
187f982db4aSGavin Atkinson 			alarmtimer(0);
188*cc361f65SGavin Atkinson 			(void)xsignal(SIGALRM, SIG_DFL);
189f982db4aSGavin Atkinson 			siglongjmp(toplevel, 1);
190f982db4aSGavin Atkinson 		}
191f982db4aSGavin Atkinson #endif	/* !STANDALONE_PROGRESS */
192f982db4aSGavin Atkinson 	}
193f982db4aSGavin Atkinson 	/*
194f982db4aSGavin Atkinson 	 * Always set the handler even if we are not the foreground process.
195f982db4aSGavin Atkinson 	 */
196f982db4aSGavin Atkinson #ifdef STANDALONE_PROGRESS
197f982db4aSGavin Atkinson 	if (progress) {
198f982db4aSGavin Atkinson #else
199f982db4aSGavin Atkinson 	if (quit_time > 0 || progress) {
200f982db4aSGavin Atkinson #endif /* !STANDALONE_PROGRESS */
201f982db4aSGavin Atkinson 		if (flag == -1) {
202f982db4aSGavin Atkinson 			(void)xsignal_restart(SIGALRM, updateprogressmeter, 1);
203f982db4aSGavin Atkinson 			alarmtimer(1);		/* set alarm timer for 1 Hz */
204f982db4aSGavin Atkinson 		} else if (flag == 1) {
205f982db4aSGavin Atkinson 			alarmtimer(0);
206*cc361f65SGavin Atkinson 			(void)xsignal(SIGALRM, SIG_DFL);
207f982db4aSGavin Atkinson 		}
208f982db4aSGavin Atkinson 	}
209f982db4aSGavin Atkinson #ifndef NO_PROGRESS
210f982db4aSGavin Atkinson 	if (!progress)
211f982db4aSGavin Atkinson 		return;
212f982db4aSGavin Atkinson 	len = 0;
213f982db4aSGavin Atkinson 
214f982db4aSGavin Atkinson 	/*
215f982db4aSGavin Atkinson 	 * print progress bar only if we are foreground process.
216f982db4aSGavin Atkinson 	 */
217f982db4aSGavin Atkinson 	if (! foregroundproc())
218f982db4aSGavin Atkinson 		return;
219f982db4aSGavin Atkinson 
220f982db4aSGavin Atkinson 	len += snprintf(buf + len, BUFLEFT, "\r");
221f982db4aSGavin Atkinson 	if (prefix)
222f982db4aSGavin Atkinson 	  len += snprintf(buf + len, BUFLEFT, "%s", prefix);
223f982db4aSGavin Atkinson 	if (filesize > 0) {
224f982db4aSGavin Atkinson 		ratio = (int)((double)cursize * 100.0 / (double)filesize);
225f982db4aSGavin Atkinson 		ratio = MAX(ratio, 0);
226f982db4aSGavin Atkinson 		ratio = MIN(ratio, 100);
227f982db4aSGavin Atkinson 		len += snprintf(buf + len, BUFLEFT, "%3d%% ", ratio);
228f982db4aSGavin Atkinson 
229f982db4aSGavin Atkinson 			/*
230f982db4aSGavin Atkinson 			 * calculate the length of the `*' bar, ensuring that
231f982db4aSGavin Atkinson 			 * the number of stars won't exceed the buffer size
232f982db4aSGavin Atkinson 			 */
233*cc361f65SGavin Atkinson 		barlength = MIN((int)(sizeof(buf) - 1), ttywidth) - BAROVERHEAD;
234f982db4aSGavin Atkinson 		if (prefix)
235*cc361f65SGavin Atkinson 			barlength -= (int)strlen(prefix);
236f982db4aSGavin Atkinson 		if (barlength > 0) {
237f982db4aSGavin Atkinson 			i = barlength * ratio / 100;
238f982db4aSGavin Atkinson 			len += snprintf(buf + len, BUFLEFT,
239*cc361f65SGavin Atkinson 			    "|%.*s%*s|", i, stars, (int)(barlength - i), "");
240f982db4aSGavin Atkinson 		}
241f982db4aSGavin Atkinson 	}
242f982db4aSGavin Atkinson 
243f982db4aSGavin Atkinson 	abbrevsize = cursize;
244*cc361f65SGavin Atkinson 	for (i = 0; abbrevsize >= 100000 && i < NSUFFIXES; i++)
245f982db4aSGavin Atkinson 		abbrevsize >>= 10;
246*cc361f65SGavin Atkinson 	if (i == NSUFFIXES)
247*cc361f65SGavin Atkinson 		i--;
248*cc361f65SGavin Atkinson 	len += snprintf(buf + len, BUFLEFT, " " LLFP("5") " %-3s ",
249f982db4aSGavin Atkinson 	    (LLT)abbrevsize,
250*cc361f65SGavin Atkinson 	    suffixes[i]);
251f982db4aSGavin Atkinson 
252f982db4aSGavin Atkinson 	timersub(&now, &start, &td);
253f982db4aSGavin Atkinson 	elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
254f982db4aSGavin Atkinson 
255f982db4aSGavin Atkinson 	bytespersec = 0;
256f982db4aSGavin Atkinson 	if (bytes > 0) {
257f982db4aSGavin Atkinson 		bytespersec = bytes;
258f982db4aSGavin Atkinson 		if (elapsed > 0.0)
259f982db4aSGavin Atkinson 			bytespersec /= elapsed;
260f982db4aSGavin Atkinson 	}
261*cc361f65SGavin Atkinson 	for (i = 1; bytespersec >= 1024000 && i < NSUFFIXES; i++)
262f982db4aSGavin Atkinson 		bytespersec >>= 10;
263f982db4aSGavin Atkinson 	len += snprintf(buf + len, BUFLEFT,
264*cc361f65SGavin Atkinson 	    " " LLFP("3") ".%02d %.2sB/s ",
265f982db4aSGavin Atkinson 	    (LLT)(bytespersec / 1024),
266f982db4aSGavin Atkinson 	    (int)((bytespersec % 1024) * 100 / 1024),
267*cc361f65SGavin Atkinson 	    suffixes[i]);
268f982db4aSGavin Atkinson 
269f982db4aSGavin Atkinson 	if (filesize > 0) {
270f982db4aSGavin Atkinson 		if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
271f982db4aSGavin Atkinson 			len += snprintf(buf + len, BUFLEFT, "   --:-- ETA");
272f982db4aSGavin Atkinson 		} else if (wait.tv_sec >= STALLTIME) {
273f982db4aSGavin Atkinson 			len += snprintf(buf + len, BUFLEFT, " - stalled -");
274f982db4aSGavin Atkinson 		} else {
275f982db4aSGavin Atkinson 			remaining = (int)
276f982db4aSGavin Atkinson 			    ((filesize - restart_point) / (bytes / elapsed) -
277f982db4aSGavin Atkinson 			    elapsed);
278f982db4aSGavin Atkinson 			if (remaining >= 100 * SECSPERHOUR)
279f982db4aSGavin Atkinson 				len += snprintf(buf + len, BUFLEFT,
280f982db4aSGavin Atkinson 				    "   --:-- ETA");
281f982db4aSGavin Atkinson 			else {
282f982db4aSGavin Atkinson 				i = remaining / SECSPERHOUR;
283f982db4aSGavin Atkinson 				if (i)
284f982db4aSGavin Atkinson 					len += snprintf(buf + len, BUFLEFT,
285f982db4aSGavin Atkinson 					    "%2d:", i);
286f982db4aSGavin Atkinson 				else
287f982db4aSGavin Atkinson 					len += snprintf(buf + len, BUFLEFT,
288f982db4aSGavin Atkinson 					    "   ");
289f982db4aSGavin Atkinson 				i = remaining % SECSPERHOUR;
290f982db4aSGavin Atkinson 				len += snprintf(buf + len, BUFLEFT,
291f982db4aSGavin Atkinson 				    "%02d:%02d ETA", i / 60, i % 60);
292f982db4aSGavin Atkinson 			}
293f982db4aSGavin Atkinson 		}
294f982db4aSGavin Atkinson 	}
295f982db4aSGavin Atkinson 	if (flag == 1)
296f982db4aSGavin Atkinson 		len += snprintf(buf + len, BUFLEFT, "\n");
297f982db4aSGavin Atkinson 	(void)write(fileno(ttyout), buf, len);
298f982db4aSGavin Atkinson 
299f982db4aSGavin Atkinson #endif	/* !NO_PROGRESS */
300f982db4aSGavin Atkinson }
301f982db4aSGavin Atkinson 
302f982db4aSGavin Atkinson #ifndef STANDALONE_PROGRESS
303f982db4aSGavin Atkinson /*
304f982db4aSGavin Atkinson  * Display transfer statistics.
305f982db4aSGavin Atkinson  * Requires start to be initialised by progressmeter(-1),
306f982db4aSGavin Atkinson  * direction to be defined by xfer routines, and filesize and bytes
307f982db4aSGavin Atkinson  * to be updated by xfer routines
308f982db4aSGavin Atkinson  * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr
309f982db4aSGavin Atkinson  * instead of ttyout.
310f982db4aSGavin Atkinson  */
311f982db4aSGavin Atkinson void
312f982db4aSGavin Atkinson ptransfer(int siginfo)
313f982db4aSGavin Atkinson {
314f982db4aSGavin Atkinson 	struct timeval now, td, wait;
315f982db4aSGavin Atkinson 	double elapsed;
316f982db4aSGavin Atkinson 	off_t bytespersec;
317*cc361f65SGavin Atkinson 	int remaining, hh, i;
318*cc361f65SGavin Atkinson 	size_t len;
319f982db4aSGavin Atkinson 
320f982db4aSGavin Atkinson 	char buf[256];		/* Work variable for transfer status. */
321f982db4aSGavin Atkinson 
322f982db4aSGavin Atkinson 	if (!verbose && !progress && !siginfo)
323f982db4aSGavin Atkinson 		return;
324f982db4aSGavin Atkinson 
325f982db4aSGavin Atkinson 	(void)gettimeofday(&now, NULL);
326f982db4aSGavin Atkinson 	timersub(&now, &start, &td);
327f982db4aSGavin Atkinson 	elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
328f982db4aSGavin Atkinson 	bytespersec = 0;
329f982db4aSGavin Atkinson 	if (bytes > 0) {
330f982db4aSGavin Atkinson 		bytespersec = bytes;
331f982db4aSGavin Atkinson 		if (elapsed > 0.0)
332f982db4aSGavin Atkinson 			bytespersec /= elapsed;
333f982db4aSGavin Atkinson 	}
334f982db4aSGavin Atkinson 	len = 0;
335f982db4aSGavin Atkinson 	len += snprintf(buf + len, BUFLEFT, LLF " byte%s %s in ",
336f982db4aSGavin Atkinson 	    (LLT)bytes, bytes == 1 ? "" : "s", direction);
337f982db4aSGavin Atkinson 	remaining = (int)elapsed;
338f982db4aSGavin Atkinson 	if (remaining > SECSPERDAY) {
339f982db4aSGavin Atkinson 		int days;
340f982db4aSGavin Atkinson 
341f982db4aSGavin Atkinson 		days = remaining / SECSPERDAY;
342f982db4aSGavin Atkinson 		remaining %= SECSPERDAY;
343f982db4aSGavin Atkinson 		len += snprintf(buf + len, BUFLEFT,
344f982db4aSGavin Atkinson 		    "%d day%s ", days, days == 1 ? "" : "s");
345f982db4aSGavin Atkinson 	}
346f982db4aSGavin Atkinson 	hh = remaining / SECSPERHOUR;
347f982db4aSGavin Atkinson 	remaining %= SECSPERHOUR;
348f982db4aSGavin Atkinson 	if (hh)
349f982db4aSGavin Atkinson 		len += snprintf(buf + len, BUFLEFT, "%2d:", hh);
350f982db4aSGavin Atkinson 	len += snprintf(buf + len, BUFLEFT,
351f982db4aSGavin Atkinson 	    "%02d:%02d ", remaining / 60, remaining % 60);
352f982db4aSGavin Atkinson 
353*cc361f65SGavin Atkinson 	for (i = 1; bytespersec >= 1024000 && i < NSUFFIXES; i++)
354f982db4aSGavin Atkinson 		bytespersec >>= 10;
355*cc361f65SGavin Atkinson 	if (i == NSUFFIXES)
356*cc361f65SGavin Atkinson 		i--;
357*cc361f65SGavin Atkinson 	len += snprintf(buf + len, BUFLEFT, "(" LLF ".%02d %.2sB/s)",
358f982db4aSGavin Atkinson 	    (LLT)(bytespersec / 1024),
359f982db4aSGavin Atkinson 	    (int)((bytespersec % 1024) * 100 / 1024),
360*cc361f65SGavin Atkinson 	    suffixes[i]);
361f982db4aSGavin Atkinson 
362f982db4aSGavin Atkinson 	if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0
363f982db4aSGavin Atkinson 	    && bytes + restart_point <= filesize) {
364f982db4aSGavin Atkinson 		remaining = (int)((filesize - restart_point) /
365f982db4aSGavin Atkinson 				  (bytes / elapsed) - elapsed);
366f982db4aSGavin Atkinson 		hh = remaining / SECSPERHOUR;
367f982db4aSGavin Atkinson 		remaining %= SECSPERHOUR;
368f982db4aSGavin Atkinson 		len += snprintf(buf + len, BUFLEFT, "  ETA: ");
369f982db4aSGavin Atkinson 		if (hh)
370f982db4aSGavin Atkinson 			len += snprintf(buf + len, BUFLEFT, "%2d:", hh);
371f982db4aSGavin Atkinson 		len += snprintf(buf + len, BUFLEFT, "%02d:%02d",
372f982db4aSGavin Atkinson 		    remaining / 60, remaining % 60);
373f982db4aSGavin Atkinson 		timersub(&now, &lastupdate, &wait);
374f982db4aSGavin Atkinson 		if (wait.tv_sec >= STALLTIME)
375f982db4aSGavin Atkinson 			len += snprintf(buf + len, BUFLEFT, "  (stalled)");
376f982db4aSGavin Atkinson 	}
377f982db4aSGavin Atkinson 	len += snprintf(buf + len, BUFLEFT, "\n");
378f982db4aSGavin Atkinson 	(void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, len);
379f982db4aSGavin Atkinson }
380f982db4aSGavin Atkinson 
381f982db4aSGavin Atkinson /*
382f982db4aSGavin Atkinson  * SIG{INFO,QUIT} handler to print transfer stats if a transfer is in progress
383f982db4aSGavin Atkinson  */
384f982db4aSGavin Atkinson void
385f982db4aSGavin Atkinson psummary(int notused)
386f982db4aSGavin Atkinson {
387f982db4aSGavin Atkinson 	int oerrno = errno;
388f982db4aSGavin Atkinson 
389f982db4aSGavin Atkinson 	if (bytes > 0) {
390f982db4aSGavin Atkinson 		if (fromatty)
391f982db4aSGavin Atkinson 			write(fileno(ttyout), "\n", 1);
392f982db4aSGavin Atkinson 		ptransfer(1);
393f982db4aSGavin Atkinson 	}
394f982db4aSGavin Atkinson 	errno = oerrno;
395f982db4aSGavin Atkinson }
396f982db4aSGavin Atkinson #endif	/* !STANDALONE_PROGRESS */
397f982db4aSGavin Atkinson 
398f982db4aSGavin Atkinson 
399f982db4aSGavin Atkinson /*
400f982db4aSGavin Atkinson  * Set the SIGALRM interval timer for wait seconds, 0 to disable.
401f982db4aSGavin Atkinson  */
402f982db4aSGavin Atkinson void
403f982db4aSGavin Atkinson alarmtimer(int wait)
404f982db4aSGavin Atkinson {
405f982db4aSGavin Atkinson 	struct itimerval itv;
406f982db4aSGavin Atkinson 
407f982db4aSGavin Atkinson 	itv.it_value.tv_sec = wait;
408f982db4aSGavin Atkinson 	itv.it_value.tv_usec = 0;
409f982db4aSGavin Atkinson 	itv.it_interval = itv.it_value;
410f982db4aSGavin Atkinson 	setitimer(ITIMER_REAL, &itv, NULL);
411f982db4aSGavin Atkinson }
412f982db4aSGavin Atkinson 
413f982db4aSGavin Atkinson 
414f982db4aSGavin Atkinson /*
415f982db4aSGavin Atkinson  * Install a POSIX signal handler, allowing the invoker to set whether
416f982db4aSGavin Atkinson  * the signal should be restartable or not
417f982db4aSGavin Atkinson  */
418f982db4aSGavin Atkinson sigfunc
419f982db4aSGavin Atkinson xsignal_restart(int sig, sigfunc func, int restartable)
420f982db4aSGavin Atkinson {
421*cc361f65SGavin Atkinson #ifdef ultrix	/* XXX: this is lame - how do we test sigvec vs. sigaction? */
422*cc361f65SGavin Atkinson 	struct sigvec vec, ovec;
423*cc361f65SGavin Atkinson 
424*cc361f65SGavin Atkinson 	vec.sv_handler = func;
425*cc361f65SGavin Atkinson 	sigemptyset(&vec.sv_mask);
426*cc361f65SGavin Atkinson 	vec.sv_flags = 0;
427*cc361f65SGavin Atkinson 	if (sigvec(sig, &vec, &ovec) < 0)
428*cc361f65SGavin Atkinson 		return (SIG_ERR);
429*cc361f65SGavin Atkinson 	return (ovec.sv_handler);
430*cc361f65SGavin Atkinson #else	/* ! ultrix */
431f982db4aSGavin Atkinson 	struct sigaction act, oact;
432f982db4aSGavin Atkinson 	act.sa_handler = func;
433f982db4aSGavin Atkinson 
434f982db4aSGavin Atkinson 	sigemptyset(&act.sa_mask);
435f982db4aSGavin Atkinson #if defined(SA_RESTART)			/* 4.4BSD, Posix(?), SVR4 */
436f982db4aSGavin Atkinson 	act.sa_flags = restartable ? SA_RESTART : 0;
437f982db4aSGavin Atkinson #elif defined(SA_INTERRUPT)		/* SunOS 4.x */
438f982db4aSGavin Atkinson 	act.sa_flags = restartable ? 0 : SA_INTERRUPT;
439f982db4aSGavin Atkinson #else
440f982db4aSGavin Atkinson #error "system must have SA_RESTART or SA_INTERRUPT"
441f982db4aSGavin Atkinson #endif
442f982db4aSGavin Atkinson 	if (sigaction(sig, &act, &oact) < 0)
443f982db4aSGavin Atkinson 		return (SIG_ERR);
444f982db4aSGavin Atkinson 	return (oact.sa_handler);
445*cc361f65SGavin Atkinson #endif	/* ! ultrix */
446f982db4aSGavin Atkinson }
447f982db4aSGavin Atkinson 
448f982db4aSGavin Atkinson /*
449f982db4aSGavin Atkinson  * Install a signal handler with the `restartable' flag set dependent upon
450f982db4aSGavin Atkinson  * which signal is being set. (This is a wrapper to xsignal_restart())
451f982db4aSGavin Atkinson  */
452f982db4aSGavin Atkinson sigfunc
453f982db4aSGavin Atkinson xsignal(int sig, sigfunc func)
454f982db4aSGavin Atkinson {
455f982db4aSGavin Atkinson 	int restartable;
456f982db4aSGavin Atkinson 
457f982db4aSGavin Atkinson 	/*
458f982db4aSGavin Atkinson 	 * Some signals print output or change the state of the process.
459f982db4aSGavin Atkinson 	 * There should be restartable, so that reads and writes are
460f982db4aSGavin Atkinson 	 * not affected.  Some signals should cause program flow to change;
461f982db4aSGavin Atkinson 	 * these signals should not be restartable, so that the system call
462f982db4aSGavin Atkinson 	 * will return with EINTR, and the program will go do something
463f982db4aSGavin Atkinson 	 * different.  If the signal handler calls longjmp() or siglongjmp(),
464f982db4aSGavin Atkinson 	 * it doesn't matter if it's restartable.
465f982db4aSGavin Atkinson 	 */
466f982db4aSGavin Atkinson 
467f982db4aSGavin Atkinson 	switch(sig) {
468f982db4aSGavin Atkinson #ifdef SIGINFO
469f982db4aSGavin Atkinson 	case SIGINFO:
470f982db4aSGavin Atkinson #endif
471f982db4aSGavin Atkinson 	case SIGQUIT:
472f982db4aSGavin Atkinson 	case SIGUSR1:
473f982db4aSGavin Atkinson 	case SIGUSR2:
474f982db4aSGavin Atkinson 	case SIGWINCH:
475f982db4aSGavin Atkinson 		restartable = 1;
476f982db4aSGavin Atkinson 		break;
477f982db4aSGavin Atkinson 
478f982db4aSGavin Atkinson 	case SIGALRM:
479f982db4aSGavin Atkinson 	case SIGINT:
480f982db4aSGavin Atkinson 	case SIGPIPE:
481f982db4aSGavin Atkinson 		restartable = 0;
482f982db4aSGavin Atkinson 		break;
483f982db4aSGavin Atkinson 
484f982db4aSGavin Atkinson 	default:
485f982db4aSGavin Atkinson 		/*
486f982db4aSGavin Atkinson 		 * This is unpleasant, but I don't know what would be better.
487f982db4aSGavin Atkinson 		 * Right now, this "can't happen"
488f982db4aSGavin Atkinson 		 */
489*cc361f65SGavin Atkinson 		errx(1, "xsignal_restart: called with signal %d", sig);
490f982db4aSGavin Atkinson 	}
491f982db4aSGavin Atkinson 
492f982db4aSGavin Atkinson 	return(xsignal_restart(sig, func, restartable));
493f982db4aSGavin Atkinson }
494