]> git.cameronkatri.com Git - bsd-progress.git/commitdiff
Abstract out progress bar support into progressbar.[ch], for inclusion
authorjhawk <jhawk@NetBSD.org>
Tue, 21 Jan 2003 16:08:06 +0000 (16:08 +0000)
committerjhawk <jhawk@NetBSD.org>
Tue, 21 Jan 2003 16:08:06 +0000 (16:08 +0000)
in external programs (conditionalized on -DSTANDALONE_PROGRESS).

The following moved from util.c to progressbar.c:
  alarmtimer(), progressmeter(), psummary(), ptransfer(),
  xsignal(), xsignal_restart()

The following moved from extern.h and ftp_var.h to progressbar.h:
  STALLTIME, verbose, fromatty, progress, quit_time, ttywidth

include/progressbar.h [new file with mode: 0644]
progressbar.c [new file with mode: 0644]

diff --git a/include/progressbar.h b/include/progressbar.h
new file mode 100644 (file)
index 0000000..f0f8485
--- /dev/null
@@ -0,0 +1,164 @@
+/*     $NetBSD: progressbar.h,v 1.1 2003/01/21 16:08:08 jhawk Exp $    */
+
+/*-
+ * Copyright (c) 1996-2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the NetBSD
+ *     Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1985, 1989, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     from: ftp_var.h 8.4 (Berkeley) 10/9/94
+ *      from: extern.h 8.3 (Berkeley) 10/9/94
+ */
+
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef STANDALONE_PROGRESS
+#include <setjmp.h>
+#endif /* !STANDALONE_PROGRESS */
+
+#ifndef        GLOBAL
+#define        GLOBAL  extern
+#endif
+
+
+#define        STALLTIME       5       /* # of seconds of no xfer before "stalling" */
+
+typedef void (*sigfunc)(int);
+
+
+GLOBAL FILE   *ttyout;         /* stdout, or stderr if retrieving to stdout */
+
+GLOBAL int     progress;       /* display transfer progress bar */
+GLOBAL int     ttywidth;       /* width of tty */
+
+GLOBAL off_t   bytes;          /* current # of bytes read */
+GLOBAL off_t   filesize;       /* size of file being transferred */
+GLOBAL off_t   restart_point;  /* offset to restart transfer */
+
+
+#ifndef        STANDALONE_PROGRESS
+GLOBAL int     fromatty;       /* input is from a terminal */
+GLOBAL int     verbose;        /* print messages coming back from server */
+GLOBAL int     quit_time;      /* maximum time to wait if stalled */
+
+GLOBAL char   *direction;      /* direction transfer is occurring */
+
+GLOBAL sigjmp_buf toplevel;    /* non-local goto stuff for cmd scanner */
+#endif /* !STANDALONE_PROGRESS */
+
+void   alarmtimer(int);
+void   progressmeter(int);
+sigfunc        xsignal(int, sigfunc);
+sigfunc        xsignal_restart(int, sigfunc, int);
+
+#ifndef STANDALONE_PROGRESS
+int    foregroundproc(void);
+void   psummary(int);
+void   ptransfer(int);
+#endif /* !STANDALONE_PROGRESS */
+
+
+#ifdef NO_LONG_LONG
+# define LLF           "%ld"
+# define LLFP(x)       "%" x "ld"
+# define LLT           long
+# define ULLF          "%lu"
+# define ULLFP(x)      "%" x "lu"
+# define ULLT          unsigned long
+#else
+# define LLF           "%lld"
+# define LLFP(x)       "%" x "lld"
+# define LLT           long long
+# define ULLF          "%llu"
+# define ULLFP(x)      "%" x "llu"
+# define ULLT          unsigned long long
+#endif
diff --git a/progressbar.c b/progressbar.c
new file mode 100644 (file)
index 0000000..c1a3a28
--- /dev/null
@@ -0,0 +1,496 @@
+/*     $NetBSD: progressbar.c,v 1.1 2003/01/21 16:08:08 jhawk Exp $    */
+
+/*-
+ * Copyright (c) 1997-2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the NetBSD
+ *     Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1985, 1989, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: progressbar.c,v 1.1 2003/01/21 16:08:08 jhawk Exp $");
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Misc support routines
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <tzfile.h>
+#include <unistd.h>
+
+#include "progressbar.h"
+
+#if !defined(NO_PROGRESS) && !defined(STANDALONE_PROGRESS)
+/*
+ * return non-zero if we're the current foreground process
+ */
+int
+foregroundproc(void)
+{
+       static pid_t pgrp = -1;
+
+       if (pgrp == -1)
+               pgrp = getpgrp();
+
+       return (tcgetpgrp(fileno(ttyout)) == pgrp);
+}
+#endif /* !defined(NO_PROGRESS) && !defined(STANDALONE_PROGRESS) */
+
+
+#ifndef        NO_PROGRESS
+static void updateprogressmeter(int);
+
+/*
+ * SIGALRM handler to update the progress meter
+ */
+static void
+updateprogressmeter(int dummy)
+{
+       int oerrno = errno;
+
+       progressmeter(0);
+       errno = oerrno;
+}
+#endif /* NO_PROGRESS */
+
+
+/*
+ * List of order of magnitude prefixes.
+ * The last is `P', as 2^64 = 16384 Petabytes
+ */
+static const char prefixes[] = " KMGTP";
+
+/*
+ * Display a transfer progress bar if progress is non-zero.
+ * SIGALRM is hijacked for use by this function.
+ * - Before the transfer, set filesize to size of file (or -1 if unknown),
+ *   and call with flag = -1. This starts the once per second timer,
+ *   and a call to updateprogressmeter() upon SIGALRM.
+ * - During the transfer, updateprogressmeter will call progressmeter
+ *   with flag = 0
+ * - After the transfer, call with flag = 1
+ */
+static struct timeval start;
+static struct timeval lastupdate;
+
+#define        BUFLEFT (sizeof(buf) - len)
+
+void
+progressmeter(int flag)
+{
+       static off_t lastsize;
+       off_t cursize;
+       struct timeval now, wait;
+#ifndef NO_PROGRESS
+       struct timeval td;
+       off_t abbrevsize, bytespersec;
+       double elapsed;
+       int ratio, barlength, i, len, remaining;
+
+                       /*
+                        * Work variables for progress bar.
+                        *
+                        * XXX: if the format of the progress bar changes
+                        *      (especially the number of characters in the
+                        *      `static' portion of it), be sure to update
+                        *      these appropriately.
+                        */
+       char            buf[256];       /* workspace for progress bar */
+#define        BAROVERHEAD     43              /* non `*' portion of progress bar */
+                                       /*
+                                        * stars should contain at least
+                                        * sizeof(buf) - BAROVERHEAD entries
+                                        */
+       static const char       stars[] =
+"*****************************************************************************"
+"*****************************************************************************"
+"*****************************************************************************";
+
+#endif
+
+       if (flag == -1) {
+               (void)gettimeofday(&start, NULL);
+               lastupdate = start;
+               lastsize = restart_point;
+       }
+
+       (void)gettimeofday(&now, NULL);
+       cursize = bytes + restart_point;
+       timersub(&now, &lastupdate, &wait);
+       if (cursize > lastsize) {
+               lastupdate = now;
+               lastsize = cursize;
+               wait.tv_sec = 0;
+       } else {
+#ifndef STANDALONE_PROGRESS
+               if (quit_time > 0 && wait.tv_sec > quit_time) {
+                       len = snprintf(buf, sizeof(buf), "\r\n%s: "
+                           "transfer aborted because stalled for %lu sec.\r\n",
+                           getprogname(), (unsigned long)wait.tv_sec);
+                       (void)write(fileno(ttyout), buf, len);
+                       (void)xsignal(SIGALRM, SIG_DFL);
+                       alarmtimer(0);
+                       siglongjmp(toplevel, 1);
+               }
+#endif /* !STANDALONE_PROGRESS */
+       }
+       /*
+        * Always set the handler even if we are not the foreground process.
+        */
+#ifdef STANDALONE_PROGRESS
+       if (progress) {
+#else
+       if (quit_time > 0 || progress) {
+#endif /* !STANDALONE_PROGRESS */
+               if (flag == -1) {
+                       (void)xsignal_restart(SIGALRM, updateprogressmeter, 1);
+                       alarmtimer(1);          /* set alarm timer for 1 Hz */
+               } else if (flag == 1) {
+                       (void)xsignal(SIGALRM, SIG_DFL);
+                       alarmtimer(0);
+               }
+       }
+#ifndef NO_PROGRESS
+       if (!progress)
+               return;
+       len = 0;
+
+#ifndef STANDALONE_PROGRESS
+       /*
+        * print progress bar only if we are foreground process.
+        */
+       if (! foregroundproc())
+               return;
+#endif /* !STANDALONE_PROGRESS */
+
+
+       len += snprintf(buf + len, BUFLEFT, "\r");
+       if (filesize > 0) {
+               ratio = (int)((double)cursize * 100.0 / (double)filesize);
+               ratio = MAX(ratio, 0);
+               ratio = MIN(ratio, 100);
+               len += snprintf(buf + len, BUFLEFT, "%3d%% ", ratio);
+
+                       /*
+                        * calculate the length of the `*' bar, ensuring that
+                        * the number of stars won't exceed the buffer size 
+                        */
+               barlength = MIN(sizeof(buf) - 1, ttywidth) - BAROVERHEAD;
+               if (barlength > 0) {
+                       i = barlength * ratio / 100;
+                       len += snprintf(buf + len, BUFLEFT,
+                           "|%.*s%*s|", i, stars, barlength - i, "");
+               }
+       }
+
+       abbrevsize = cursize;
+       for (i = 0; abbrevsize >= 100000 && i < sizeof(prefixes); i++)
+               abbrevsize >>= 10;
+       len += snprintf(buf + len, BUFLEFT, " " LLFP("5") " %c%c ",
+           (LLT)abbrevsize,
+           prefixes[i],
+           i == 0 ? ' ' : 'B');
+
+       timersub(&now, &start, &td);
+       elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
+
+       bytespersec = 0;
+       if (bytes > 0) {
+               bytespersec = bytes;
+               if (elapsed > 0.0)
+                       bytespersec /= elapsed;
+       }
+       for (i = 1; bytespersec >= 1024000 && i < sizeof(prefixes); i++)
+               bytespersec >>= 10;
+       len += snprintf(buf + len, BUFLEFT,
+           " " LLFP("3") ".%02d %cB/s ",
+           (LLT)(bytespersec / 1024),
+           (int)((bytespersec % 1024) * 100 / 1024),
+           prefixes[i]);
+
+       if (filesize > 0) {
+               if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
+                       len += snprintf(buf + len, BUFLEFT, "   --:-- ETA");
+               } else if (wait.tv_sec >= STALLTIME) {
+                       len += snprintf(buf + len, BUFLEFT, " - stalled -");
+               } else {
+                       remaining = (int)
+                           ((filesize - restart_point) / (bytes / elapsed) -
+                           elapsed);
+                       if (remaining >= 100 * SECSPERHOUR)
+                               len += snprintf(buf + len, BUFLEFT,
+                                   "   --:-- ETA");
+                       else {
+                               i = remaining / SECSPERHOUR;
+                               if (i)
+                                       len += snprintf(buf + len, BUFLEFT,
+                                           "%2d:", i);
+                               else
+                                       len += snprintf(buf + len, BUFLEFT,
+                                           "   ");
+                               i = remaining % SECSPERHOUR;
+                               len += snprintf(buf + len, BUFLEFT,
+                                   "%02d:%02d ETA", i / 60, i % 60);
+                       }
+               }
+       }
+       if (flag == 1)
+               len += snprintf(buf + len, BUFLEFT, "\n");
+       (void)write(fileno(ttyout), buf, len);
+
+#endif /* !NO_PROGRESS */
+}
+
+#ifndef STANDALONE_PROGRESS
+/*
+ * Display transfer statistics.
+ * Requires start to be initialised by progressmeter(-1),
+ * direction to be defined by xfer routines, and filesize and bytes
+ * to be updated by xfer routines
+ * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr
+ * instead of ttyout.
+ */
+void
+ptransfer(int siginfo)
+{
+       struct timeval now, td, wait;
+       double elapsed;
+       off_t bytespersec;
+       int remaining, hh, i, len;
+
+       char buf[256];          /* Work variable for transfer status. */
+
+       if (!verbose && !progress && !siginfo)
+               return;
+
+       (void)gettimeofday(&now, NULL);
+       timersub(&now, &start, &td);
+       elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
+       bytespersec = 0;
+       if (bytes > 0) {
+               bytespersec = bytes;
+               if (elapsed > 0.0)
+                       bytespersec /= elapsed;
+       }
+       len = 0;
+       len += snprintf(buf + len, BUFLEFT, LLF " byte%s %s in ",
+           (LLT)bytes, bytes == 1 ? "" : "s", direction);
+       remaining = (int)elapsed;
+       if (remaining > SECSPERDAY) {
+               int days;
+
+               days = remaining / SECSPERDAY;
+               remaining %= SECSPERDAY;
+               len += snprintf(buf + len, BUFLEFT,
+                   "%d day%s ", days, days == 1 ? "" : "s");
+       }
+       hh = remaining / SECSPERHOUR;
+       remaining %= SECSPERHOUR;
+       if (hh)
+               len += snprintf(buf + len, BUFLEFT, "%2d:", hh);
+       len += snprintf(buf + len, BUFLEFT,
+           "%02d:%02d ", remaining / 60, remaining % 60);
+
+       for (i = 1; bytespersec >= 1024000 && i < sizeof(prefixes); i++)
+               bytespersec >>= 10;
+       len += snprintf(buf + len, BUFLEFT, "(" LLF ".%02d %cB/s)",
+           (LLT)(bytespersec / 1024),
+           (int)((bytespersec % 1024) * 100 / 1024),
+           prefixes[i]);
+
+       if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0
+           && bytes + restart_point <= filesize) {
+               remaining = (int)((filesize - restart_point) /
+                                 (bytes / elapsed) - elapsed);
+               hh = remaining / SECSPERHOUR;
+               remaining %= SECSPERHOUR;
+               len += snprintf(buf + len, BUFLEFT, "  ETA: ");
+               if (hh)
+                       len += snprintf(buf + len, BUFLEFT, "%2d:", hh);
+               len += snprintf(buf + len, BUFLEFT, "%02d:%02d",
+                   remaining / 60, remaining % 60);
+               timersub(&now, &lastupdate, &wait);
+               if (wait.tv_sec >= STALLTIME)
+                       len += snprintf(buf + len, BUFLEFT, "  (stalled)");
+       }
+       len += snprintf(buf + len, BUFLEFT, "\n");
+       (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, len);
+}
+
+/*
+ * SIG{INFO,QUIT} handler to print transfer stats if a transfer is in progress
+ */
+void
+psummary(int notused)
+{
+       int oerrno = errno;
+
+       if (bytes > 0) {
+               if (fromatty)
+                       write(fileno(ttyout), "\n", 1);
+               ptransfer(1);
+       }
+       errno = oerrno;
+}
+#endif /* !STANDALONE_PROGRESS */
+
+
+/*
+ * Set the SIGALRM interval timer for wait seconds, 0 to disable.
+ */
+void
+alarmtimer(int wait)
+{
+       struct itimerval itv;
+
+       itv.it_value.tv_sec = wait;
+       itv.it_value.tv_usec = 0;
+       itv.it_interval = itv.it_value;
+       setitimer(ITIMER_REAL, &itv, NULL);
+}
+
+
+/*
+ * Install a POSIX signal handler, allowing the invoker to set whether
+ * the signal should be restartable or not
+ */
+sigfunc
+xsignal_restart(int sig, sigfunc func, int restartable)
+{
+       struct sigaction act, oact;
+       act.sa_handler = func;
+
+       sigemptyset(&act.sa_mask);
+#if defined(SA_RESTART)                        /* 4.4BSD, Posix(?), SVR4 */
+       act.sa_flags = restartable ? SA_RESTART : 0;
+#elif defined(SA_INTERRUPT)            /* SunOS 4.x */
+       act.sa_flags = restartable ? 0 : SA_INTERRUPT;
+#else
+#error "system must have SA_RESTART or SA_INTERRUPT"
+#endif
+       if (sigaction(sig, &act, &oact) < 0)
+               return (SIG_ERR);
+       return (oact.sa_handler);
+}
+
+/*
+ * Install a signal handler with the `restartable' flag set dependent upon
+ * which signal is being set. (This is a wrapper to xsignal_restart())
+ */
+sigfunc
+xsignal(int sig, sigfunc func)
+{
+       int restartable;
+
+       /*
+        * Some signals print output or change the state of the process.
+        * There should be restartable, so that reads and writes are
+        * not affected.  Some signals should cause program flow to change;
+        * these signals should not be restartable, so that the system call
+        * will return with EINTR, and the program will go do something
+        * different.  If the signal handler calls longjmp() or siglongjmp(),
+        * it doesn't matter if it's restartable.
+        */
+
+       switch(sig) {
+#ifdef SIGINFO
+       case SIGINFO:
+#endif
+       case SIGQUIT:
+       case SIGUSR1:
+       case SIGUSR2:
+       case SIGWINCH:
+               restartable = 1;
+               break;
+
+       case SIGALRM:
+       case SIGINT:
+       case SIGPIPE:
+               restartable = 0;
+               break;
+
+       default:
+               /*
+                * This is unpleasant, but I don't know what would be better.
+                * Right now, this "can't happen"
+                */
+               errx(1, "xsignal_restart called with signal %d", sig);
+       }
+
+       return(xsignal_restart(sig, func, restartable));
+}