/* * Copyright (c) 1999 Theo de Raadt. All rights reserved. * Copyright (c) 1999 Aaron Campbell. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ /* * Parts from: * * Copyright (c) 1983, 1990, 1992, 1993, 1995 * 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 "includes.h" RCSID("$OpenBSD: progressmeter.c,v 1.2 2003/01/12 16:57:02 markus Exp $"); #include #include "atomicio.h" #include "progressmeter.h" /* Number of seconds before xfer considered "stalled". */ #define STALLTIME 5 /* alarm() interval for updating progress meter. */ #define PROGRESSTIME 1 /* Signal handler used for updating the progress meter. */ static void update_progress_meter(int); /* Returns non-zero if we are the foreground process. */ static int foregroundproc(void); /* Returns width of the terminal (for progress meter calculations). */ static int get_tty_width(void); /* Visual statistics about files as they are transferred. */ static void draw_progress_meter(void); /* Time a transfer started. */ static struct timeval start; /* Number of bytes of current file transferred so far. */ static volatile off_t *statbytes; /* Total size of current file. */ static off_t totalbytes; /* Name of current file being transferred. */ static char *curfile; /* Time of last update. */ static struct timeval lastupdate; /* Size at the time of the last update. */ static off_t lastsize; void start_progress_meter(char *file, off_t filesize, off_t *counter) { if ((curfile = basename(file)) == NULL) curfile = file; totalbytes = filesize; statbytes = counter; (void) gettimeofday(&start, (struct timezone *) 0); lastupdate = start; lastsize = 0; draw_progress_meter(); signal(SIGALRM, update_progress_meter); alarm(PROGRESSTIME); } void stop_progress_meter() { alarm(0); draw_progress_meter(); atomicio(write, fileno(stdout), "\n", 1); } static void update_progress_meter(int ignore) { int save_errno = errno; draw_progress_meter(); signal(SIGALRM, update_progress_meter); alarm(PROGRESSTIME); errno = save_errno; } static int foregroundproc(void) { static pid_t pgrp = -1; int ctty_pgrp; if (pgrp == -1) pgrp = getpgrp(); return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && ctty_pgrp == pgrp)); } static void draw_progress_meter() { 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; (void) gettimeofday(&now, (struct timezone *) 0); cursize = *statbytes; if (totalbytes != 0) { ratio = 100.0 * cursize / totalbytes; ratio = MAX(ratio, 0); ratio = MIN(ratio, 100); } else ratio = 100; abbrevsize = cursize; for (ai = 0; abbrevsize >= 10000 && ai < sizeof(prefixes); ai++) abbrevsize >>= 10; 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); #ifdef HAVE_LONG_LONG_INT snprintf(buf, sizeof(buf), "\r%-45.45s%.*s%3d%% %4lld%c%c %3lld.%01d%cB/s", curfile, nspaces, spaces, ratio, (long long)abbrevsize, prefixes[ai], ai == 0 ? ' ' : 'B', (long long)(bytespersec / 1024), (int)((bytespersec % 1024) * 10 / 1024), prefixes[bi] ); #else /* XXX: Handle integer overflow? */ snprintf(buf, sizeof(buf), "\r%-45.45s%.*s%3d%% %4lu%c%c %3lu.%01d%cB/s", curfile, nspaces, spaces, ratio, (u_long)abbrevsize, prefixes[ai], ai == 0 ? ' ' : 'B', (u_long)(bytespersec / 1024), (int)((bytespersec % 1024) * 10 / 1024), prefixes[bi] ); #endif 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; 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(write, fileno(stdout), buf, strlen(buf)); } static int get_tty_width(void) { struct winsize winsize; if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) return (winsize.ws_col ? winsize.ws_col : 80); else return (80); }