progressmeter.c revision 124208
117721Speter/* 217721Speter * Copyright (c) 2003 Nils Nordman. All rights reserved. 317721Speter * 417721Speter * Redistribution and use in source and binary forms, with or without 517721Speter * modification, are permitted provided that the following conditions 617721Speter * are met: 717721Speter * 1. Redistributions of source code must retain the above copyright 817721Speter * notice, this list of conditions and the following disclaimer. 917721Speter * 2. Redistributions in binary form must reproduce the above copyright 1017721Speter * notice, this list of conditions and the following disclaimer in the 1117721Speter * documentation and/or other materials provided with the distribution. 1225839Speter * 1317721Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1417721Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1517721Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1617721Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1717721Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1817721Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1917721Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2017721Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2117721Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2217721Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2317721Speter */ 2417721Speter 2517721Speter#include "includes.h" 2617721SpeterRCSID("$OpenBSD: progressmeter.c,v 1.15 2003/08/31 12:14:22 markus Exp $"); 2717721Speter 2817721Speter#include "progressmeter.h" 2917721Speter#include "atomicio.h" 3017721Speter#include "misc.h" 3117721Speter 3217721Speter#define DEFAULT_WINSIZE 80 3317721Speter#define MAX_WINSIZE 512 3417721Speter#define PADDING 1 /* padding between the progress indicators */ 3517721Speter#define UPDATE_INTERVAL 1 /* update the progress meter every second */ 3617721Speter#define STALL_TIME 5 /* we're stalled after this many seconds */ 3717721Speter 3817721Speter/* determines whether we can output to the terminal */ 3917721Speterstatic int can_output(void); 4017721Speter 4117721Speter/* formats and inserts the specified size into the given buffer */ 4217721Speterstatic void format_size(char *, int, off_t); 4317721Speterstatic void format_rate(char *, int, off_t); 4417721Speter 4517721Speter/* updates the progressmeter to reflect the current state of the transfer */ 4617721Spetervoid refresh_progress_meter(void); 4717721Speter 4817721Speter/* signal handler for updating the progress meter */ 4917721Speterstatic void update_progress_meter(int); 5017721Speter 5117721Speterstatic time_t start; /* start progress */ 5217721Speterstatic time_t last_update; /* last progress update */ 5317721Speterstatic char *file; /* name of the file being transferred */ 5417721Speterstatic off_t end_pos; /* ending position of transfer */ 5517721Speterstatic off_t cur_pos; /* transfer position as of last refresh */ 5617721Speterstatic volatile off_t *counter; /* progress counter */ 5717721Speterstatic long stalled; /* how long we have been stalled */ 5817721Speterstatic int bytes_per_second; /* current speed in bytes per second */ 5917721Speterstatic int win_size; /* terminal window size */ 6017721Speter 6117721Speter/* units for format_size */ 6217721Speterstatic const char unit[] = " KMGT"; 6317721Speter 6417721Speterstatic int 6517721Spetercan_output(void) 6617721Speter{ 6717721Speter return (getpgrp() == tcgetpgrp(STDOUT_FILENO)); 6817721Speter} 6917721Speter 7017721Speterstatic void 7117721Speterformat_rate(char *buf, int size, off_t bytes) 7217721Speter{ 7317721Speter int i; 7417721Speter 7517721Speter bytes *= 100; 7617721Speter for (i = 0; bytes >= 100*1000 && unit[i] != 'T'; i++) 7717721Speter bytes = (bytes + 512) / 1024; 7817721Speter if (i == 0) { 7917721Speter i++; 8017721Speter bytes = (bytes + 512) / 1024; 8117721Speter } 8217721Speter snprintf(buf, size, "%3lld.%1lld%c%s", 8317721Speter (int64_t) bytes / 100, 8417721Speter (int64_t) (bytes + 5) / 10 % 10, 8517721Speter unit[i], 8617721Speter i ? "B" : " "); 8717721Speter} 8817721Speter 8917721Speterstatic void 9017721Speterformat_size(char *buf, int size, off_t bytes) 9117721Speter{ 9217721Speter int i; 9317721Speter 9417721Speter for (i = 0; bytes >= 10000 && unit[i] != 'T'; i++) 9517721Speter bytes = (bytes + 512) / 1024; 9617721Speter snprintf(buf, size, "%4lld%c%s", 9717721Speter (int64_t) bytes, 9817721Speter unit[i], 9917721Speter i ? "B" : " "); 10017721Speter} 10117721Speter 10217721Spetervoid 10317721Speterrefresh_progress_meter(void) 10417721Speter{ 10517721Speter char buf[MAX_WINSIZE + 1]; 10617721Speter time_t now; 10717721Speter off_t transferred; 10817721Speter double elapsed; 10917721Speter int percent; 11017721Speter int bytes_left; 11117721Speter int cur_speed; 11217721Speter int hours, minutes, seconds; 11317721Speter int i, len; 11417721Speter int file_len; 11517721Speter 11617721Speter transferred = *counter - cur_pos; 11717721Speter cur_pos = *counter; 11817721Speter now = time(NULL); 11917721Speter bytes_left = end_pos - cur_pos; 12017721Speter 12117721Speter if (bytes_left > 0) 12217721Speter elapsed = now - last_update; 12317721Speter else 12417721Speter elapsed = now - start; 12517721Speter 12617721Speter /* calculate speed */ 12717721Speter if (elapsed != 0) 12817721Speter cur_speed = (transferred / elapsed); 12917721Speter else 13017721Speter cur_speed = 0; 13117721Speter 13217721Speter#define AGE_FACTOR 0.9 13317721Speter if (bytes_per_second != 0) { 13417721Speter bytes_per_second = (bytes_per_second * AGE_FACTOR) + 13517721Speter (cur_speed * (1.0 - AGE_FACTOR)); 13617721Speter } else 13717721Speter bytes_per_second = cur_speed; 13817721Speter 13917721Speter /* filename */ 14017721Speter buf[0] = '\0'; 14117721Speter file_len = win_size - 35; 14217721Speter if (file_len > 0) { 14317721Speter len = snprintf(buf, file_len + 1, "\r%s", file); 14417721Speter if (len < 0) 14517721Speter len = 0; 14617721Speter for (i = len; i < file_len; i++ ) 14717721Speter buf[i] = ' '; 14817721Speter buf[file_len] = '\0'; 14917721Speter } 15017721Speter 15117721Speter /* percent of transfer done */ 15217721Speter if (end_pos != 0) 15317721Speter percent = ((float)cur_pos / end_pos) * 100; 15417721Speter else 15517721Speter percent = 100; 15617721Speter snprintf(buf + strlen(buf), win_size - strlen(buf), 15717721Speter " %3d%% ", percent); 15817721Speter 15917721Speter /* amount transferred */ 16017721Speter format_size(buf + strlen(buf), win_size - strlen(buf), 16117721Speter cur_pos); 16217721Speter strlcat(buf, " ", win_size); 16317721Speter 16417721Speter /* bandwidth usage */ 16517721Speter format_rate(buf + strlen(buf), win_size - strlen(buf), 16617721Speter bytes_per_second); 16717721Speter strlcat(buf, "/s ", win_size); 16817721Speter 16917721Speter /* ETA */ 17017721Speter if (!transferred) 17117721Speter stalled += elapsed; 17217721Speter else 17317721Speter stalled = 0; 17417721Speter 17517721Speter if (stalled >= STALL_TIME) 17617721Speter strlcat(buf, "- stalled -", win_size); 17717721Speter else if (bytes_per_second == 0 && bytes_left) 17817721Speter strlcat(buf, " --:-- ETA", win_size); 17917721Speter else { 18017721Speter if (bytes_left > 0) 18117721Speter seconds = bytes_left / bytes_per_second; 18217721Speter else 18317721Speter seconds = elapsed; 18417721Speter 18517721Speter hours = seconds / 3600; 18617721Speter seconds -= hours * 3600; 18717721Speter minutes = seconds / 60; 18817721Speter seconds -= minutes * 60; 18917721Speter 19017721Speter if (hours != 0) 19117721Speter snprintf(buf + strlen(buf), win_size - strlen(buf), 19217721Speter "%d:%02d:%02d", hours, minutes, seconds); 19317721Speter else 19417721Speter snprintf(buf + strlen(buf), win_size - strlen(buf), 19517721Speter " %02d:%02d", minutes, seconds); 19617721Speter 19717721Speter if (bytes_left > 0) 19817721Speter strlcat(buf, " ETA", win_size); 19917721Speter else 20017721Speter strlcat(buf, " ", win_size); 20117721Speter } 20217721Speter 20317721Speter atomicio(vwrite, STDOUT_FILENO, buf, win_size); 20417721Speter last_update = now; 20517721Speter} 20617721Speter 20717721Speterstatic void 20817721Speterupdate_progress_meter(int ignore) 20917721Speter{ 21017721Speter int save_errno; 21117721Speter 21217721Speter save_errno = errno; 21317721Speter 21417721Speter if (can_output()) 21517721Speter refresh_progress_meter(); 21617721Speter 21717721Speter signal(SIGALRM, update_progress_meter); 21817721Speter alarm(UPDATE_INTERVAL); 21917721Speter errno = save_errno; 22017721Speter} 22117721Speter 22217721Spetervoid 22317721Speterstart_progress_meter(char *f, off_t filesize, off_t *stat) 22417721Speter{ 22517721Speter struct winsize winsize; 22617721Speter 22717721Speter start = last_update = time(NULL); 22817721Speter file = f; 22917721Speter end_pos = filesize; 23017721Speter cur_pos = 0; 23117721Speter counter = stat; 23217721Speter stalled = 0; 23317721Speter bytes_per_second = 0; 23417721Speter 23517721Speter if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 && 23617721Speter winsize.ws_col != 0) { 23717721Speter if (winsize.ws_col > MAX_WINSIZE) 23817721Speter win_size = MAX_WINSIZE; 23917721Speter else 24017721Speter win_size = winsize.ws_col; 24117721Speter } else 24217721Speter win_size = DEFAULT_WINSIZE; 24317721Speter win_size += 1; /* trailing \0 */ 24417721Speter 24517721Speter if (can_output()) 24617721Speter refresh_progress_meter(); 24717721Speter 24817721Speter signal(SIGALRM, update_progress_meter); 24917721Speter alarm(UPDATE_INTERVAL); 25017721Speter} 25117721Speter 25217721Spetervoid 25317721Speterstop_progress_meter(void) 25417721Speter{ 25517721Speter alarm(0); 25617721Speter 25717721Speter if (!can_output()) 25817721Speter return; 25917721Speter 26017721Speter /* Ensure we complete the progress */ 26117721Speter if (cur_pos != end_pos) 26217721Speter refresh_progress_meter(); 26317721Speter 26417721Speter atomicio(vwrite, STDOUT_FILENO, "\n", 1); 26517721Speter} 26617721Speter