From 06ef75bf0c2449ab4880f6ff40729497782e6e60 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Sat, 2 Aug 2003 23:28:38 +1000 Subject: - markus@cvs.openbsd.org 2003/07/29 18:24:00 [LICENCE progressmeter.c] replace 4 clause BSD licensed progressmeter code with a replacement from Nils Nordman and myself; ok deraadt@ (copied from OpenBSD an re-applied portable changes) --- progressmeter.c | 407 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 200 insertions(+), 207 deletions(-) (limited to 'progressmeter.c') diff --git a/progressmeter.c b/progressmeter.c index 6aa225ad..170d869f 100644 --- a/progressmeter.c +++ b/progressmeter.c @@ -1,6 +1,5 @@ /* - * Copyright (c) 1999 Theo de Raadt. All rights reserved. - * Copyright (c) 1999 Aaron Campbell. All rights reserved. + * Copyright (c) 2003 Nils Nordman. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,248 +22,242 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* - * 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. - */ - #include "includes.h" -RCSID("$OpenBSD: progressmeter.c,v 1.8 2003/06/28 16:23:06 deraadt Exp $"); - -#ifdef HAVE_LIBGEN_H -#include -#endif +RCSID("$OpenBSD: progressmeter.c,v 1.13 2003/07/31 22:34:03 markus Exp $"); -#include "atomicio.h" #include "progressmeter.h" +#include "atomicio.h" #include "misc.h" -/* Number of seconds before xfer considered "stalled". */ -#define STALLTIME 5 -/* alarm() interval for updating progress meter. */ -#define PROGRESSTIME 1 +#define DEFAULT_WINSIZE 80 +#define MAX_WINSIZE 512 +#define PADDING 1 /* padding between the progress indicators */ +#define UPDATE_INTERVAL 1 /* update the progress meter every second */ +#define STALL_TIME 5 /* we're stalled after this many seconds */ -/* Signal handler used for updating the progress meter. */ -static void update_progress_meter(int); +/* determines whether we can output to the terminal */ +static int can_output(void); -/* Returns non-zero if we are the foreground process. */ -static int foregroundproc(void); +/* formats and inserts the specified size into the given buffer */ +static void format_size(char *, int, off_t); +static void format_rate(char *, int, off_t); -/* Returns width of the terminal (for progress meter calculations). */ -static int get_tty_width(void); +/* updates the progressmeter to reflect the current state of the transfer */ +void refresh_progress_meter(void); -/* Visual statistics about files as they are transferred. */ -static void draw_progress_meter(void); - -/* Time a transfer started. */ -static struct timeval start; +/* signal handler for updating the progress meter */ +static void update_progress_meter(int); -/* Number of bytes of current file transferred so far. */ -static volatile off_t *statbytes; +static time_t start; /* start progress */ +static time_t last_update; /* last progress update */ +static char *file; /* name of the file being transferred */ +static off_t end_pos; /* ending position of transfer */ +static off_t cur_pos; /* transfer position as of last refresh */ +static volatile off_t *counter; /* progress counter */ +static long stalled; /* how long we have been stalled */ +static int bytes_per_second; /* current speed in bytes per second */ +static int win_size; /* terminal window size */ -/* Total size of current file. */ -static off_t totalbytes; +/* units for format_size */ +static const char unit[] = " KMGT"; -/* Name of current file being transferred. */ -static char *curfile; +static int +can_output(void) +{ + return (getpgrp() == tcgetpgrp(STDOUT_FILENO)); +} -/* Time of last update. */ -static struct timeval lastupdate; +static void +format_rate(char *buf, int size, off_t bytes) +{ + int i; + + bytes *= 100; + for (i = 0; bytes >= 100*1000 && unit[i] != 'T'; i++) + bytes = (bytes + 512) / 1024; + if (i == 0) { + i++; + bytes = (bytes + 512) / 1024; + } + snprintf(buf, size, "%3lld.%1lld%c%s", + (int64_t) bytes / 100, + (int64_t) (bytes + 5) / 10 % 10, + unit[i], + i ? "B" : " "); +} -/* Size at the time of the last update. */ -static off_t lastsize; +static void +format_size(char *buf, int size, off_t bytes) +{ + int i; + + for (i = 0; bytes >= 10000 && unit[i] != 'T'; i++) + bytes = (bytes + 512) / 1024; + snprintf(buf, size, "%4lld%c%s", + (int64_t) bytes, + unit[i], + i ? "B" : " "); +} void -start_progress_meter(char *file, off_t filesize, off_t *counter) +refresh_progress_meter(void) { - if ((curfile = basename(file)) == NULL) - curfile = file; + char buf[MAX_WINSIZE + 1]; + time_t now; + off_t transferred; + double elapsed; + int percent; + int bytes_left; + int cur_speed; + int hours, minutes, seconds; + int i, len; + int file_len; + + transferred = *counter - cur_pos; + cur_pos = *counter; + now = time(NULL); + bytes_left = end_pos - cur_pos; + + if (bytes_left > 0) + elapsed = now - last_update; + else + elapsed = now - start; - totalbytes = filesize; - statbytes = counter; - (void) gettimeofday(&start, (struct timezone *) 0); - lastupdate = start; - lastsize = 0; + /* calculate speed */ + if (elapsed != 0) + cur_speed = (transferred / elapsed); + else + cur_speed = 0; - draw_progress_meter(); - mysignal(SIGALRM, update_progress_meter); - alarm(PROGRESSTIME); -} +#define AGE_FACTOR 0.9 + if (bytes_per_second != 0) { + bytes_per_second = (bytes_per_second * AGE_FACTOR) + + (cur_speed * (1.0 - AGE_FACTOR)); + } else + bytes_per_second = cur_speed; + + /* filename */ + buf[0] = '\0'; + file_len = win_size - 35; + if (file_len > 0) { + len = snprintf(buf, file_len, "\r%s", file); + for (i = len; i < file_len; i++ ) + buf[i] = ' '; + buf[file_len] = '\0'; + } -void -stop_progress_meter(void) -{ - alarm(0); - draw_progress_meter(); - if (foregroundproc() != 0) - atomicio(vwrite, fileno(stdout), "\n", 1); + /* percent of transfer done */ + if (end_pos != 0) + percent = ((float)cur_pos / end_pos) * 100; + else + percent = 100; + snprintf(buf + strlen(buf), win_size - strlen(buf), + " %3d%% ", percent); + + /* amount transferred */ + format_size(buf + strlen(buf), win_size - strlen(buf), + cur_pos); + strlcat(buf, " ", win_size); + + /* bandwidth usage */ + format_rate(buf + strlen(buf), win_size - strlen(buf), + bytes_per_second); + strlcat(buf, "/s ", win_size); + + /* ETA */ + if (!transferred) + stalled += elapsed; + else + stalled = 0; + + if (stalled >= STALL_TIME) + strlcat(buf, "- stalled -", win_size); + else if (bytes_per_second == 0 && bytes_left) + strlcat(buf, " --:-- ETA", win_size); + else { + if (bytes_left > 0) + seconds = bytes_left / bytes_per_second; + else + seconds = elapsed; + + hours = seconds / 3600; + seconds -= hours * 3600; + minutes = seconds / 60; + seconds -= minutes * 60; + + if (hours != 0) + snprintf(buf + strlen(buf), win_size - strlen(buf), + "%d:%02d:%02d", hours, minutes, seconds); + else + snprintf(buf + strlen(buf), win_size - strlen(buf), + " %02d:%02d", minutes, seconds); + + if (bytes_left > 0) + strlcat(buf, " ETA", win_size); + else + strlcat(buf, " ", win_size); + } + + atomicio(vwrite, STDOUT_FILENO, buf, win_size); + last_update = now; } static void update_progress_meter(int ignore) { - int save_errno = errno; + int save_errno; + + save_errno = errno; + + if (can_output()) + refresh_progress_meter(); - draw_progress_meter(); mysignal(SIGALRM, update_progress_meter); - alarm(PROGRESSTIME); + alarm(UPDATE_INTERVAL); errno = save_errno; } -static int -foregroundproc(void) -{ - static pid_t pgrp = -1; - int ctty_pgrp; - - if (pgrp == -1) - pgrp = getpgrp(); - -#ifdef HAVE_TCGETPGRP - return ((ctty_pgrp = tcgetpgrp(STDOUT_FILENO)) != -1 && - ctty_pgrp == pgrp); -#else - return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && - ctty_pgrp == pgrp)); -#endif -} - -static void -draw_progress_meter(void) +void +start_progress_meter(char *f, off_t filesize, off_t *stat) { - static const char spaces[] = " " - " " - " " - " " - " " - " "; - static const char prefixes[] = " KMGTP"; - struct timeval now, td, wait; - off_t cursize, abbrevsize, bytespersec; - double elapsed; - int ratio, remaining, i, ai, bi, nspaces; - char buf[512]; - - if (foregroundproc() == 0) - return; + struct winsize winsize; - (void) gettimeofday(&now, (struct timezone *) 0); - cursize = *statbytes; - if (totalbytes != 0) { - ratio = 100.0 * cursize / totalbytes; - ratio = MAX(ratio, 0); - ratio = MIN(ratio, 100); + start = last_update = time(NULL); + file = f; + end_pos = filesize; + cur_pos = 0; + counter = stat; + stalled = 0; + bytes_per_second = 0; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 && + winsize.ws_col != 0) { + if (winsize.ws_col > MAX_WINSIZE) + win_size = MAX_WINSIZE; + else + win_size = winsize.ws_col; } else - ratio = 100; - - abbrevsize = cursize; - for (ai = 0; abbrevsize >= 10000 && ai < sizeof(prefixes); ai++) - abbrevsize >>= 10; + win_size = DEFAULT_WINSIZE; + win_size += 1; /* trailing \0 */ - timersub(&now, &lastupdate, &wait); - if (cursize > lastsize) { - lastupdate = now; - lastsize = cursize; - wait.tv_sec = 0; - } - timersub(&now, &start, &td); - elapsed = td.tv_sec + (td.tv_usec / 1000000.0); - - bytespersec = 0; - if (cursize > 0) { - bytespersec = cursize; - if (elapsed > 0.0) - bytespersec /= elapsed; - } - for (bi = 1; bytespersec >= 1024000 && bi < sizeof(prefixes); bi++) - bytespersec >>= 10; - - nspaces = MIN(get_tty_width() - 79, sizeof(spaces) - 1); - - snprintf(buf, sizeof(buf), - "\r%-45.45s%.*s%3d%% %4lld%c%c %3lld.%01d%cB/s", - curfile, - nspaces, - spaces, - ratio, - (int64_t)abbrevsize, - prefixes[ai], - ai == 0 ? ' ' : 'B', - (int64_t)(bytespersec / 1024), - (int)((bytespersec % 1024) * 10 / 1024), - prefixes[bi] - ); - - if (cursize <= 0 || elapsed <= 0.0 || cursize > totalbytes) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " --:-- ETA"); - } else if (wait.tv_sec >= STALLTIME) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " - stalled -"); - } else { - if (cursize != totalbytes) - remaining = (int)(totalbytes / (cursize / elapsed) - - elapsed); - else - remaining = elapsed; + if (can_output()) + refresh_progress_meter(); - i = remaining / 3600; - if (i) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "%2d:", i); - else - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " "); - i = remaining % 3600; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "%02d:%02d%s", i / 60, i % 60, - (cursize != totalbytes) ? " ETA" : " "); - } - atomicio(vwrite, fileno(stdout), buf, strlen(buf)); + mysignal(SIGALRM, update_progress_meter); + alarm(UPDATE_INTERVAL); } -static int -get_tty_width(void) +void +stop_progress_meter(void) { - struct winsize winsize; + alarm(0); - if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) - return (winsize.ws_col ? winsize.ws_col : 80); - else - return (80); + if (!can_output()) + return; + + /* Ensure we complete the progress */ + if (cur_pos != end_pos) + refresh_progress_meter(); + + atomicio(vwrite, STDOUT_FILENO, "\n", 1); } -- cgit v1.2.1