File: [local] / src / usr.bin / ftp / Attic / progressmeter.c (download)
Revision 1.3, Sun May 12 21:56:27 2019 UTC (5 years, 1 month ago) by kmos
Branch: MAIN
Changes since 1.2: +2 -2 lines
Make the "xx bytes received in y time" message go to the right place
by using the login_info() function the other messages use.
OK florian@
|
/* $OpenBSD: progressmeter.c,v 1.3 2019/05/12 21:56:27 kmos Exp $ */
/*
* Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org>
* 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
* 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.
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <err.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "ftp.h"
#define DEFAULT_WINSIZE 80
#define MAX_WINSIZE 512
#define UPDATE_INTERVAL 1 /* update the progress meter every second */
#define STALL_TIME 5 /* we're stalled after this many seconds */
time_t monotime(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);
/* window resizing */
static void sig_winch(int);
static void setscreensize(void);
/* updates the progressmeter to reflect the current state of the transfer */
void refresh_progress_meter(void);
/* signal handler for updating the progress meter */
static void update_progress_meter(int);
static const char *title; /* short title for the start of progress bar */
static time_t start; /* start progress */
static time_t last_update; /* last progress update */
static off_t start_pos; /* initial position of transfer */
static off_t end_pos; /* ending position of transfer */
static off_t cur_pos; /* transfer position as of last refresh */
static off_t offset; /* initial offset from start_pos */
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 */
static volatile sig_atomic_t win_resized; /* for window resizing */
static const char *filename; /* To be displayed in non-verbose mode */
/* units for format_size */
static const char unit[] = " KMGT";
time_t
monotime(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
err(1, "monotime");
return ts.tv_sec;
}
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",
(long long) (bytes + 5) / 100,
(long long) (bytes + 5) / 10 % 10,
unit[i],
"B");
}
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",
(long long) bytes,
unit[i],
i ? "B" : " ");
}
void
refresh_progress_meter(void)
{
char buf[MAX_WINSIZE + 1];
const char *dot = "";
time_t now;
off_t transferred, bytes_left;
double elapsed;
int len, cur_speed, hours, minutes, seconds, barlength, i;
int percent, overhead = 30;
transferred = *counter - (cur_pos ? cur_pos : start_pos);
cur_pos = *counter;
now = monotime();
bytes_left = end_pos - cur_pos;
if (bytes_left > 0)
elapsed = now - last_update;
else {
elapsed = now - start;
/* Calculate true total speed when done */
transferred = end_pos - start_pos;
bytes_per_second = 0;
}
/* calculate speed */
if (elapsed != 0)
cur_speed = (transferred / elapsed);
else
cur_speed = transferred;
#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;
buf[0] = '\0';
/* title */
if (!verbose && title != NULL) {
len = strlen(title);
if (len < 7)
len = 7;
else if (len > 12) {
len = 12;
dot = "...";
overhead += 3;
}
snprintf(buf, sizeof buf, "\r%-*.*s%s ", len, len, title, dot);
overhead += len + 1;
} else
snprintf(buf, sizeof buf, "\r");
if (end_pos == 0 || cur_pos == end_pos)
percent = 100;
else
percent = ((float)cur_pos / end_pos) * 100;
/* filename and percent */
if (!verbose && filename != NULL) {
len = strlen(filename);
if (len < 12)
len = 12;
else if (len > 25) {
len = 22;
dot = "...";
overhead += 3;
}
snprintf(buf + strlen(buf), sizeof buf - strlen(buf),
"%-*.*s%s %3d%% ", len, len, filename, dot, percent);
overhead += len + 1;
} else
snprintf(buf, sizeof buf, "\r%3d%% ", percent);
/* bar */
barlength = win_size - overhead;
if (barlength > 0) {
i = barlength * percent / 100;
snprintf(buf + strlen(buf), sizeof buf - strlen(buf),
"|%.*s%*s| ", i,
"*******************************************************"
"*******************************************************"
"*******************************************************"
"*******************************************************"
"*******************************************************"
"*******************************************************"
"*******************************************************",
barlength - i, "");
}
/* amount transferred */
format_size(buf + strlen(buf), win_size - strlen(buf), cur_pos);
strlcat(buf, " ", 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);
}
write(STDERR_FILENO, buf, strlen(buf));
last_update = now;
}
static void
update_progress_meter(int ignore)
{
int save_errno;
save_errno = errno;
if (win_resized) {
setscreensize();
win_resized = 0;
}
refresh_progress_meter();
signal(SIGALRM, update_progress_meter);
alarm(UPDATE_INTERVAL);
errno = save_errno;
}
void
start_progress_meter(const char *fn, const char *t, off_t filesize, off_t *ctr)
{
start = last_update = monotime();
start_pos = *ctr;
offset = *ctr;
cur_pos = 0;
end_pos = 0;
counter = ctr;
stalled = 0;
bytes_per_second = 0;
filename = fn;
title = t;
/*
* Suppress progressmeter if filesize isn't known when
* Content-Length header has bogus values.
*/
if (filesize <= 0)
return;
end_pos = filesize;
setscreensize();
refresh_progress_meter();
signal(SIGALRM, update_progress_meter);
signal(SIGWINCH, sig_winch);
alarm(UPDATE_INTERVAL);
}
void
stop_progress_meter(void)
{
char rate_str[32];
double elapsed;
alarm(0);
/* Ensure we complete the progress */
if (end_pos && cur_pos != end_pos)
refresh_progress_meter();
if (end_pos)
write(STDERR_FILENO, "\n", 1);
if (!verbose)
return;
elapsed = monotime() - start;
if (end_pos == 0) {
if (elapsed != 0)
bytes_per_second = *counter / elapsed;
else
bytes_per_second = *counter;
}
format_rate(rate_str, sizeof rate_str, bytes_per_second);
log_info("%lld bytes received in %.2f seconds (%s/s)\n",
(end_pos) ? cur_pos - offset : *counter, elapsed, rate_str);
}
static void
sig_winch(int sig)
{
win_resized = 1;
}
static void
setscreensize(void)
{
struct winsize winsize;
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
win_size = DEFAULT_WINSIZE;
win_size += 1; /* trailing \0 */
}