2001-11-22 23:59:52 -05:00
|
|
|
|
/* Download progress.
|
2002-04-11 21:23:23 -04:00
|
|
|
|
Copyright (C) 2001, 2002 Free Software Foundation, Inc.
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
|
|
|
|
This file is part of GNU Wget.
|
|
|
|
|
|
|
|
|
|
GNU Wget is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
2001-11-27 21:24:22 -05:00
|
|
|
|
(at your option) any later version.
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
|
|
|
|
GNU Wget is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with Wget; if not, write to the Free Software
|
2002-05-17 22:16:36 -04:00
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
|
|
|
|
|
In addition, as a special exception, the Free Software Foundation
|
|
|
|
|
gives permission to link the code of its release of Wget with the
|
|
|
|
|
OpenSSL project's "OpenSSL" library (or with modified versions of it
|
|
|
|
|
that use the same license as the "OpenSSL" library), and distribute
|
|
|
|
|
the linked executables. You must obey the GNU General Public License
|
|
|
|
|
in all respects for all of the code used other than "OpenSSL". If you
|
|
|
|
|
modify this file, you may extend this exception to your version of the
|
|
|
|
|
file, but you are not obligated to do so. If you do not wish to do
|
|
|
|
|
so, delete this exception statement from your version. */
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
|
# include <string.h>
|
|
|
|
|
#else
|
|
|
|
|
# include <strings.h>
|
|
|
|
|
#endif /* HAVE_STRING_H */
|
|
|
|
|
#include <assert.h>
|
2001-11-24 22:10:34 -05:00
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
|
# include <unistd.h>
|
|
|
|
|
#endif
|
2001-12-06 05:45:27 -05:00
|
|
|
|
#ifdef HAVE_SIGNAL_H
|
|
|
|
|
# include <signal.h>
|
|
|
|
|
#endif
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
|
|
|
|
#include "wget.h"
|
|
|
|
|
#include "progress.h"
|
|
|
|
|
#include "utils.h"
|
|
|
|
|
#include "retr.h"
|
|
|
|
|
|
|
|
|
|
struct progress_implementation {
|
|
|
|
|
char *name;
|
2002-04-11 13:49:32 -04:00
|
|
|
|
void *(*create) PARAMS ((long, long));
|
2003-09-15 17:14:15 -04:00
|
|
|
|
void (*update) PARAMS ((void *, long, double));
|
|
|
|
|
void (*finish) PARAMS ((void *, double));
|
2002-04-11 13:49:32 -04:00
|
|
|
|
void (*set_params) PARAMS ((const char *));
|
2001-11-22 23:59:52 -05:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Necessary forward declarations. */
|
|
|
|
|
|
2001-11-23 11:12:53 -05:00
|
|
|
|
static void *dot_create PARAMS ((long, long));
|
2003-09-15 17:14:15 -04:00
|
|
|
|
static void dot_update PARAMS ((void *, long, double));
|
|
|
|
|
static void dot_finish PARAMS ((void *, double));
|
2001-11-23 11:12:53 -05:00
|
|
|
|
static void dot_set_params PARAMS ((const char *));
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
|
|
|
|
static void *bar_create PARAMS ((long, long));
|
2003-09-15 17:14:15 -04:00
|
|
|
|
static void bar_update PARAMS ((void *, long, double));
|
|
|
|
|
static void bar_finish PARAMS ((void *, double));
|
2001-11-22 23:59:52 -05:00
|
|
|
|
static void bar_set_params PARAMS ((const char *));
|
|
|
|
|
|
|
|
|
|
static struct progress_implementation implementations[] = {
|
2001-11-23 11:12:53 -05:00
|
|
|
|
{ "dot", dot_create, dot_update, dot_finish, dot_set_params },
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{ "bar", bar_create, bar_update, bar_finish, bar_set_params }
|
|
|
|
|
};
|
|
|
|
|
static struct progress_implementation *current_impl;
|
2001-12-08 20:24:41 -05:00
|
|
|
|
static int current_impl_locked;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2001-11-27 05:29:45 -05:00
|
|
|
|
/* Progress implementation used by default. Can be overriden in
|
|
|
|
|
wgetrc or by the fallback one. */
|
|
|
|
|
|
|
|
|
|
#define DEFAULT_PROGRESS_IMPLEMENTATION "bar"
|
|
|
|
|
|
|
|
|
|
/* Fallnback progress implementation should be something that works
|
2001-11-23 11:12:53 -05:00
|
|
|
|
under all display types. If you put something other than "dot"
|
|
|
|
|
here, remember that bar_set_params tries to switch to this if we're
|
|
|
|
|
not running on a TTY. So changing this to "bar" could cause
|
|
|
|
|
infloop. */
|
|
|
|
|
|
2001-11-27 05:29:45 -05:00
|
|
|
|
#define FALLBACK_PROGRESS_IMPLEMENTATION "dot"
|
2001-11-23 11:12:53 -05:00
|
|
|
|
|
2001-11-22 23:59:52 -05:00
|
|
|
|
/* Return non-zero if NAME names a valid progress bar implementation.
|
|
|
|
|
The characters after the first : will be ignored. */
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
valid_progress_implementation_p (const char *name)
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
struct progress_implementation *pi = implementations;
|
|
|
|
|
char *colon = strchr (name, ':');
|
|
|
|
|
int namelen = colon ? colon - name : strlen (name);
|
|
|
|
|
|
2003-09-19 10:08:37 -04:00
|
|
|
|
for (i = 0; i < countof (implementations); i++, pi++)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
if (!strncmp (pi->name, name, namelen))
|
|
|
|
|
return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set the progress implementation to NAME. */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
set_progress_implementation (const char *name)
|
|
|
|
|
{
|
2001-11-23 11:12:53 -05:00
|
|
|
|
int i, namelen;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
struct progress_implementation *pi = implementations;
|
2001-11-23 11:12:53 -05:00
|
|
|
|
char *colon;
|
|
|
|
|
|
|
|
|
|
if (!name)
|
|
|
|
|
name = DEFAULT_PROGRESS_IMPLEMENTATION;
|
|
|
|
|
|
|
|
|
|
colon = strchr (name, ':');
|
|
|
|
|
namelen = colon ? colon - name : strlen (name);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2003-09-19 10:08:37 -04:00
|
|
|
|
for (i = 0; i < countof (implementations); i++, pi++)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
if (!strncmp (pi->name, name, namelen))
|
|
|
|
|
{
|
|
|
|
|
current_impl = pi;
|
2001-12-06 02:14:35 -05:00
|
|
|
|
current_impl_locked = 0;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
|
|
|
|
if (colon)
|
|
|
|
|
/* We call pi->set_params even if colon is NULL because we
|
|
|
|
|
want to give the implementation a chance to set up some
|
|
|
|
|
things it needs to run. */
|
|
|
|
|
++colon;
|
|
|
|
|
|
|
|
|
|
if (pi->set_params)
|
|
|
|
|
pi->set_params (colon);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
|
2001-12-06 02:14:35 -05:00
|
|
|
|
static int output_redirected;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
progress_schedule_redirect (void)
|
|
|
|
|
{
|
|
|
|
|
output_redirected = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-22 23:59:52 -05:00
|
|
|
|
/* Create a progress gauge. INITIAL is the number of bytes the
|
|
|
|
|
download starts from (zero if the download starts from scratch).
|
|
|
|
|
TOTAL is the expected total number of bytes in this download. If
|
|
|
|
|
TOTAL is zero, it means that the download size is not known in
|
|
|
|
|
advance. */
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
progress_create (long initial, long total)
|
|
|
|
|
{
|
2001-12-06 02:14:35 -05:00
|
|
|
|
/* Check if the log status has changed under our feet. */
|
|
|
|
|
if (output_redirected)
|
|
|
|
|
{
|
|
|
|
|
if (!current_impl_locked)
|
|
|
|
|
set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
|
|
|
|
|
output_redirected = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-22 23:59:52 -05:00
|
|
|
|
return current_impl->create (initial, total);
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-26 07:49:10 -05:00
|
|
|
|
/* Inform the progress gauge of newly received bytes. DLTIME is the
|
|
|
|
|
time in milliseconds since the beginning of the download. */
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
|
|
|
|
void
|
2003-09-15 17:14:15 -04:00
|
|
|
|
progress_update (void *progress, long howmuch, double dltime)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
2001-11-26 07:49:10 -05:00
|
|
|
|
current_impl->update (progress, howmuch, dltime);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Tell the progress gauge to clean up. Calling this will free the
|
|
|
|
|
PROGRESS object, the further use of which is not allowed. */
|
|
|
|
|
|
|
|
|
|
void
|
2003-09-15 17:14:15 -04:00
|
|
|
|
progress_finish (void *progress, double dltime)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
2001-11-26 07:49:10 -05:00
|
|
|
|
current_impl->finish (progress, dltime);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Dot-printing. */
|
|
|
|
|
|
|
|
|
|
struct dot_progress {
|
|
|
|
|
long initial_length; /* how many bytes have been downloaded
|
|
|
|
|
previously. */
|
|
|
|
|
long total_length; /* expected total byte count when the
|
|
|
|
|
download finishes */
|
|
|
|
|
|
|
|
|
|
int accumulated;
|
|
|
|
|
|
|
|
|
|
int rows; /* number of rows printed so far */
|
|
|
|
|
int dots; /* number of dots printed in this row */
|
2003-09-15 17:14:15 -04:00
|
|
|
|
double last_timer_value;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Dot-progress backend for progress_create. */
|
|
|
|
|
|
|
|
|
|
static void *
|
2001-11-23 11:12:53 -05:00
|
|
|
|
dot_create (long initial, long total)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
2003-10-31 09:55:50 -05:00
|
|
|
|
struct dot_progress *dp = xnew0 (struct dot_progress);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
dp->initial_length = initial;
|
|
|
|
|
dp->total_length = total;
|
|
|
|
|
|
|
|
|
|
if (dp->initial_length)
|
|
|
|
|
{
|
|
|
|
|
int dot_bytes = opt.dot_bytes;
|
|
|
|
|
long row_bytes = opt.dot_bytes * opt.dots_in_line;
|
|
|
|
|
|
|
|
|
|
int remainder = (int) (dp->initial_length % row_bytes);
|
|
|
|
|
long skipped = dp->initial_length - remainder;
|
|
|
|
|
|
|
|
|
|
if (skipped)
|
|
|
|
|
{
|
2001-11-24 23:46:26 -05:00
|
|
|
|
int skipped_k = (int) (skipped / 1024); /* skipped amount in K */
|
|
|
|
|
int skipped_k_len = numdigit (skipped_k);
|
|
|
|
|
if (skipped_k_len < 5)
|
|
|
|
|
skipped_k_len = 5;
|
|
|
|
|
|
|
|
|
|
/* Align the [ skipping ... ] line with the dots. To do
|
|
|
|
|
that, insert the number of spaces equal to the number of
|
|
|
|
|
digits in the skipped amount in K. */
|
2001-11-26 15:07:13 -05:00
|
|
|
|
logprintf (LOG_VERBOSE, _("\n%*s[ skipping %dK ]"),
|
|
|
|
|
2 + skipped_k_len, "", skipped_k);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logprintf (LOG_VERBOSE, "\n%5ldK", skipped / 1024);
|
|
|
|
|
for (; remainder >= dot_bytes; remainder -= dot_bytes)
|
|
|
|
|
{
|
|
|
|
|
if (dp->dots % opt.dot_spacing == 0)
|
|
|
|
|
logputs (LOG_VERBOSE, " ");
|
|
|
|
|
logputs (LOG_VERBOSE, ",");
|
|
|
|
|
++dp->dots;
|
|
|
|
|
}
|
|
|
|
|
assert (dp->dots < opt.dots_in_line);
|
|
|
|
|
|
|
|
|
|
dp->accumulated = remainder;
|
|
|
|
|
dp->rows = skipped / row_bytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_percentage (long bytes, long expected)
|
|
|
|
|
{
|
|
|
|
|
int percentage = (int)(100.0 * bytes / expected);
|
|
|
|
|
logprintf (LOG_VERBOSE, "%3d%%", percentage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2003-09-15 17:14:15 -04:00
|
|
|
|
print_download_speed (struct dot_progress *dp, long bytes, double dltime)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
2001-11-23 11:12:53 -05:00
|
|
|
|
logprintf (LOG_VERBOSE, " %s",
|
2001-11-26 07:49:10 -05:00
|
|
|
|
retr_rate (bytes, dltime - dp->last_timer_value, 1));
|
|
|
|
|
dp->last_timer_value = dltime;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Dot-progress backend for progress_update. */
|
|
|
|
|
|
|
|
|
|
static void
|
2003-09-15 17:14:15 -04:00
|
|
|
|
dot_update (void *progress, long howmuch, double dltime)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
|
|
|
|
struct dot_progress *dp = progress;
|
|
|
|
|
int dot_bytes = opt.dot_bytes;
|
|
|
|
|
long row_bytes = opt.dot_bytes * opt.dots_in_line;
|
|
|
|
|
|
|
|
|
|
log_set_flush (0);
|
|
|
|
|
|
|
|
|
|
dp->accumulated += howmuch;
|
|
|
|
|
for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
|
|
|
|
|
{
|
|
|
|
|
if (dp->dots == 0)
|
|
|
|
|
logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
|
|
|
|
|
|
|
|
|
|
if (dp->dots % opt.dot_spacing == 0)
|
|
|
|
|
logputs (LOG_VERBOSE, " ");
|
|
|
|
|
logputs (LOG_VERBOSE, ".");
|
|
|
|
|
|
|
|
|
|
++dp->dots;
|
|
|
|
|
if (dp->dots >= opt.dots_in_line)
|
|
|
|
|
{
|
2001-11-26 15:07:13 -05:00
|
|
|
|
long row_qty = row_bytes;
|
|
|
|
|
if (dp->rows == dp->initial_length / row_bytes)
|
|
|
|
|
row_qty -= dp->initial_length % row_bytes;
|
|
|
|
|
|
2001-11-22 23:59:52 -05:00
|
|
|
|
++dp->rows;
|
|
|
|
|
dp->dots = 0;
|
|
|
|
|
|
|
|
|
|
if (dp->total_length)
|
|
|
|
|
print_percentage (dp->rows * row_bytes, dp->total_length);
|
2001-11-26 15:07:13 -05:00
|
|
|
|
print_download_speed (dp, row_qty, dltime);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_set_flush (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Dot-progress backend for progress_finish. */
|
|
|
|
|
|
|
|
|
|
static void
|
2003-09-15 17:14:15 -04:00
|
|
|
|
dot_finish (void *progress, double dltime)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
|
|
|
|
struct dot_progress *dp = progress;
|
|
|
|
|
int dot_bytes = opt.dot_bytes;
|
|
|
|
|
long row_bytes = opt.dot_bytes * opt.dots_in_line;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
log_set_flush (0);
|
|
|
|
|
|
2001-11-27 21:44:18 -05:00
|
|
|
|
if (dp->dots == 0)
|
|
|
|
|
logprintf (LOG_VERBOSE, "\n%5ldK", dp->rows * row_bytes / 1024);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
for (i = dp->dots; i < opt.dots_in_line; i++)
|
|
|
|
|
{
|
|
|
|
|
if (i % opt.dot_spacing == 0)
|
|
|
|
|
logputs (LOG_VERBOSE, " ");
|
|
|
|
|
logputs (LOG_VERBOSE, " ");
|
|
|
|
|
}
|
|
|
|
|
if (dp->total_length)
|
|
|
|
|
{
|
|
|
|
|
print_percentage (dp->rows * row_bytes
|
|
|
|
|
+ dp->dots * dot_bytes
|
|
|
|
|
+ dp->accumulated,
|
|
|
|
|
dp->total_length);
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-26 15:07:13 -05:00
|
|
|
|
{
|
|
|
|
|
long row_qty = dp->dots * dot_bytes + dp->accumulated;
|
|
|
|
|
if (dp->rows == dp->initial_length / row_bytes)
|
|
|
|
|
row_qty -= dp->initial_length % row_bytes;
|
|
|
|
|
print_download_speed (dp, row_qty, dltime);
|
|
|
|
|
}
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2001-11-26 15:07:13 -05:00
|
|
|
|
logputs (LOG_VERBOSE, "\n\n");
|
2001-11-22 23:59:52 -05:00
|
|
|
|
log_set_flush (0);
|
|
|
|
|
|
|
|
|
|
xfree (dp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This function interprets the progress "parameters". For example,
|
2001-11-27 21:24:22 -05:00
|
|
|
|
if Wget is invoked with --progress=dot:mega, it will set the
|
2001-11-22 23:59:52 -05:00
|
|
|
|
"dot-style" to "mega". Valid styles are default, binary, mega, and
|
|
|
|
|
giga. */
|
|
|
|
|
|
|
|
|
|
static void
|
2001-11-23 11:12:53 -05:00
|
|
|
|
dot_set_params (const char *params)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
2001-11-27 21:44:18 -05:00
|
|
|
|
if (!params || !*params)
|
|
|
|
|
params = opt.dot_style;
|
|
|
|
|
|
2001-11-22 23:59:52 -05:00
|
|
|
|
if (!params)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* We use this to set the retrieval style. */
|
|
|
|
|
if (!strcasecmp (params, "default"))
|
|
|
|
|
{
|
|
|
|
|
/* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
|
|
|
|
|
line. */
|
|
|
|
|
opt.dot_bytes = 1024;
|
|
|
|
|
opt.dot_spacing = 10;
|
|
|
|
|
opt.dots_in_line = 50;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcasecmp (params, "binary"))
|
|
|
|
|
{
|
|
|
|
|
/* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
|
|
|
|
|
(384K) in a line. */
|
|
|
|
|
opt.dot_bytes = 8192;
|
|
|
|
|
opt.dot_spacing = 16;
|
|
|
|
|
opt.dots_in_line = 48;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcasecmp (params, "mega"))
|
|
|
|
|
{
|
|
|
|
|
/* "Mega" retrieval, for retrieving very long files; each dot is
|
|
|
|
|
64K, 8 dots in a cluster, 6 clusters (3M) in a line. */
|
|
|
|
|
opt.dot_bytes = 65536L;
|
|
|
|
|
opt.dot_spacing = 8;
|
|
|
|
|
opt.dots_in_line = 48;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcasecmp (params, "giga"))
|
|
|
|
|
{
|
|
|
|
|
/* "Giga" retrieval, for retrieving very very *very* long files;
|
|
|
|
|
each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
|
|
|
|
|
line. */
|
|
|
|
|
opt.dot_bytes = (1L << 20);
|
|
|
|
|
opt.dot_spacing = 8;
|
|
|
|
|
opt.dots_in_line = 32;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
fprintf (stderr,
|
|
|
|
|
_("Invalid dot style specification `%s'; leaving unchanged.\n"),
|
|
|
|
|
params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* "Thermometer" (bar) progress. */
|
|
|
|
|
|
|
|
|
|
/* Assumed screen width if we can't find the real value. */
|
|
|
|
|
#define DEFAULT_SCREEN_WIDTH 80
|
|
|
|
|
|
|
|
|
|
/* Minimum screen width we'll try to work with. If this is too small,
|
|
|
|
|
create_image will overflow the buffer. */
|
|
|
|
|
#define MINIMUM_SCREEN_WIDTH 45
|
|
|
|
|
|
|
|
|
|
static int screen_width = DEFAULT_SCREEN_WIDTH;
|
|
|
|
|
|
2003-09-15 17:14:15 -04:00
|
|
|
|
/* Size of the download speed history ring. */
|
2003-09-22 13:14:19 -04:00
|
|
|
|
#define DLSPEED_HISTORY_SIZE 20
|
2002-04-10 05:24:28 -04:00
|
|
|
|
|
2003-09-15 17:14:15 -04:00
|
|
|
|
/* The minimum time length of a history sample. By default, each
|
2003-09-22 13:14:19 -04:00
|
|
|
|
sample is at least 150ms long, which means that, over the course of
|
|
|
|
|
20 samples, "current" download speed spans at least 3s into the
|
2003-09-15 17:14:15 -04:00
|
|
|
|
past. */
|
2003-09-22 13:14:19 -04:00
|
|
|
|
#define DLSPEED_SAMPLE_MIN 150
|
2002-04-10 05:24:28 -04:00
|
|
|
|
|
2001-11-22 23:59:52 -05:00
|
|
|
|
struct bar_progress {
|
|
|
|
|
long initial_length; /* how many bytes have been downloaded
|
|
|
|
|
previously. */
|
|
|
|
|
long total_length; /* expected total byte count when the
|
|
|
|
|
download finishes */
|
|
|
|
|
long count; /* bytes downloaded so far */
|
|
|
|
|
|
2003-09-15 17:14:15 -04:00
|
|
|
|
double last_screen_update; /* time of the last screen update,
|
|
|
|
|
measured since the beginning of
|
|
|
|
|
download. */
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2001-12-06 05:45:27 -05:00
|
|
|
|
int width; /* screen width we're using at the
|
|
|
|
|
time the progress gauge was
|
|
|
|
|
created. this is different from
|
|
|
|
|
the screen_width global variable in
|
|
|
|
|
that the latter can be changed by a
|
|
|
|
|
signal. */
|
2001-11-22 23:59:52 -05:00
|
|
|
|
char *buffer; /* buffer where the bar "image" is
|
|
|
|
|
stored. */
|
2002-04-09 18:10:01 -04:00
|
|
|
|
int tick; /* counter used for drawing the
|
|
|
|
|
progress bar where the total size
|
|
|
|
|
is not known. */
|
|
|
|
|
|
|
|
|
|
/* The following variables (kept in a struct for namespace reasons)
|
2002-04-10 05:24:28 -04:00
|
|
|
|
keep track of recent download speeds. See bar_update() for
|
|
|
|
|
details. */
|
|
|
|
|
struct bar_progress_hist {
|
|
|
|
|
int pos;
|
|
|
|
|
long times[DLSPEED_HISTORY_SIZE];
|
|
|
|
|
long bytes[DLSPEED_HISTORY_SIZE];
|
2003-09-15 17:14:15 -04:00
|
|
|
|
|
|
|
|
|
/* The sum of times and bytes respectively, maintained for
|
|
|
|
|
efficiency. */
|
|
|
|
|
long total_time;
|
|
|
|
|
long total_bytes;
|
2002-04-10 05:24:28 -04:00
|
|
|
|
} hist;
|
2002-04-09 18:10:01 -04:00
|
|
|
|
|
2003-09-15 17:14:15 -04:00
|
|
|
|
double recent_start; /* timestamp of beginning of current
|
|
|
|
|
position. */
|
|
|
|
|
long recent_bytes; /* bytes downloaded so far. */
|
|
|
|
|
|
2002-04-09 18:10:01 -04:00
|
|
|
|
/* create_image() uses these to make sure that ETA information
|
|
|
|
|
doesn't flash. */
|
2003-09-15 17:14:15 -04:00
|
|
|
|
double last_eta_time; /* time of the last update to download
|
|
|
|
|
speed and ETA, measured since the
|
|
|
|
|
beginning of download. */
|
2002-04-09 18:10:01 -04:00
|
|
|
|
long last_eta_value;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
};
|
|
|
|
|
|
2003-09-15 17:14:15 -04:00
|
|
|
|
static void create_image PARAMS ((struct bar_progress *, double));
|
2001-11-22 23:59:52 -05:00
|
|
|
|
static void display_image PARAMS ((char *));
|
|
|
|
|
|
|
|
|
|
static void *
|
|
|
|
|
bar_create (long initial, long total)
|
|
|
|
|
{
|
2003-10-31 09:55:50 -05:00
|
|
|
|
struct bar_progress *bp = xnew0 (struct bar_progress);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2002-04-11 14:51:26 -04:00
|
|
|
|
/* In theory, our callers should take care of this pathological
|
|
|
|
|
case, but it can sometimes happen. */
|
|
|
|
|
if (initial > total)
|
|
|
|
|
total = initial;
|
|
|
|
|
|
2001-11-22 23:59:52 -05:00
|
|
|
|
bp->initial_length = initial;
|
|
|
|
|
bp->total_length = total;
|
2001-12-06 05:45:27 -05:00
|
|
|
|
|
|
|
|
|
/* - 1 because we don't want to use the last screen column. */
|
|
|
|
|
bp->width = screen_width - 1;
|
|
|
|
|
/* + 1 for the terminating zero. */
|
2001-11-22 23:59:52 -05:00
|
|
|
|
bp->buffer = xmalloc (bp->width + 1);
|
|
|
|
|
|
2001-11-27 21:44:18 -05:00
|
|
|
|
logputs (LOG_VERBOSE, "\n");
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
|
|
|
|
create_image (bp, 0);
|
|
|
|
|
display_image (bp->buffer);
|
|
|
|
|
|
|
|
|
|
return bp;
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-15 17:14:15 -04:00
|
|
|
|
static void update_speed_ring PARAMS ((struct bar_progress *, long, double));
|
|
|
|
|
|
2001-11-22 23:59:52 -05:00
|
|
|
|
static void
|
2003-09-15 17:14:15 -04:00
|
|
|
|
bar_update (void *progress, long howmuch, double dltime)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
|
|
|
|
struct bar_progress *bp = progress;
|
2002-04-09 18:10:01 -04:00
|
|
|
|
int force_screen_update = 0;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
|
|
|
|
bp->count += howmuch;
|
2001-11-24 13:05:57 -05:00
|
|
|
|
if (bp->total_length > 0
|
|
|
|
|
&& bp->count + bp->initial_length > bp->total_length)
|
2001-11-23 00:09:39 -05:00
|
|
|
|
/* We could be downloading more than total_length, e.g. when the
|
|
|
|
|
server sends an incorrect Content-Length header. In that case,
|
|
|
|
|
adjust bp->total_length to the new reality, so that the code in
|
|
|
|
|
create_image() that depends on total size being smaller or
|
|
|
|
|
equal to the expected size doesn't abort. */
|
2002-04-11 14:51:26 -04:00
|
|
|
|
bp->total_length = bp->initial_length + bp->count;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2003-09-15 17:14:15 -04:00
|
|
|
|
update_speed_ring (bp, howmuch, dltime);
|
2002-04-09 18:10:01 -04:00
|
|
|
|
|
2001-12-06 05:45:27 -05:00
|
|
|
|
if (screen_width - 1 != bp->width)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
2001-12-06 05:45:27 -05:00
|
|
|
|
bp->width = screen_width - 1;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
bp->buffer = xrealloc (bp->buffer, bp->width + 1);
|
2002-04-09 18:10:01 -04:00
|
|
|
|
force_screen_update = 1;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
}
|
|
|
|
|
|
2002-04-09 18:10:01 -04:00
|
|
|
|
if (dltime - bp->last_screen_update < 200 && !force_screen_update)
|
2001-12-06 05:45:27 -05:00
|
|
|
|
/* Don't update more often than five times per second. */
|
2001-11-22 23:59:52 -05:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
create_image (bp, dltime);
|
|
|
|
|
display_image (bp->buffer);
|
2002-04-09 18:10:01 -04:00
|
|
|
|
bp->last_screen_update = dltime;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2003-09-15 17:14:15 -04:00
|
|
|
|
bar_finish (void *progress, double dltime)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
|
|
|
|
struct bar_progress *bp = progress;
|
|
|
|
|
|
2002-04-11 14:51:26 -04:00
|
|
|
|
if (bp->total_length > 0
|
|
|
|
|
&& bp->count + bp->initial_length > bp->total_length)
|
|
|
|
|
/* See bar_update() for explanation. */
|
|
|
|
|
bp->total_length = bp->initial_length + bp->count;
|
|
|
|
|
|
2001-11-26 07:49:10 -05:00
|
|
|
|
create_image (bp, dltime);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
display_image (bp->buffer);
|
|
|
|
|
|
|
|
|
|
logputs (LOG_VERBOSE, "\n\n");
|
|
|
|
|
|
|
|
|
|
xfree (bp->buffer);
|
|
|
|
|
xfree (bp);
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-15 17:14:15 -04:00
|
|
|
|
/* This code attempts to maintain the notion of a "current" download
|
|
|
|
|
speed, over the course of no less than 3s. (Shorter intervals
|
|
|
|
|
produce very erratic results.)
|
|
|
|
|
|
2003-09-22 13:14:19 -04:00
|
|
|
|
To do so, it samples the speed in 150ms intervals and stores the
|
2003-09-15 17:14:15 -04:00
|
|
|
|
recorded samples in a FIFO history ring. The ring stores no more
|
2003-09-22 13:14:19 -04:00
|
|
|
|
than 20 intervals, hence the history covers the period of at least
|
|
|
|
|
three seconds and at most 20 reads into the past. This method
|
|
|
|
|
should produce reasonable results for downloads ranging from very
|
|
|
|
|
slow to very fast.
|
2003-09-15 17:14:15 -04:00
|
|
|
|
|
|
|
|
|
The idea is that for fast downloads, we get the speed over exactly
|
|
|
|
|
the last three seconds. For slow downloads (where a network read
|
2003-09-22 13:14:19 -04:00
|
|
|
|
takes more than 150ms to complete), we get the speed over a larger
|
2003-09-15 17:14:15 -04:00
|
|
|
|
time period, as large as it takes to complete thirty reads. This
|
|
|
|
|
is good because slow downloads tend to fluctuate more and a
|
2003-09-22 13:14:19 -04:00
|
|
|
|
3-second average would be too erratic. */
|
2003-09-15 17:14:15 -04:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
update_speed_ring (struct bar_progress *bp, long howmuch, double dltime)
|
|
|
|
|
{
|
|
|
|
|
struct bar_progress_hist *hist = &bp->hist;
|
|
|
|
|
double recent_age = dltime - bp->recent_start;
|
|
|
|
|
|
|
|
|
|
/* Update the download count. */
|
|
|
|
|
bp->recent_bytes += howmuch;
|
|
|
|
|
|
|
|
|
|
/* For very small time intervals, we return after having updated the
|
|
|
|
|
"recent" download count. When its age reaches or exceeds minimum
|
|
|
|
|
sample time, it will be recorded in the history ring. */
|
|
|
|
|
if (recent_age < DLSPEED_SAMPLE_MIN)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Store "recent" bytes and download time to history ring at the
|
|
|
|
|
position POS. */
|
|
|
|
|
|
|
|
|
|
/* To correctly maintain the totals, first invalidate existing data
|
|
|
|
|
(least recent in time) at this position. */
|
|
|
|
|
hist->total_time -= hist->times[hist->pos];
|
|
|
|
|
hist->total_bytes -= hist->bytes[hist->pos];
|
|
|
|
|
|
|
|
|
|
/* Now store the new data and update the totals. */
|
|
|
|
|
hist->times[hist->pos] = recent_age;
|
|
|
|
|
hist->bytes[hist->pos] = bp->recent_bytes;
|
|
|
|
|
hist->total_time += recent_age;
|
|
|
|
|
hist->total_bytes += bp->recent_bytes;
|
|
|
|
|
|
|
|
|
|
/* Start a new "recent" period. */
|
|
|
|
|
bp->recent_start = dltime;
|
|
|
|
|
bp->recent_bytes = 0;
|
|
|
|
|
|
|
|
|
|
/* Advance the current ring position. */
|
|
|
|
|
if (++hist->pos == DLSPEED_HISTORY_SIZE)
|
|
|
|
|
hist->pos = 0;
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
/* Sledgehammer check to verify that the totals are accurate. */
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
double sumt = 0, sumb = 0;
|
|
|
|
|
for (i = 0; i < DLSPEED_HISTORY_SIZE; i++)
|
|
|
|
|
{
|
|
|
|
|
sumt += hist->times[i];
|
|
|
|
|
sumb += hist->bytes[i];
|
|
|
|
|
}
|
|
|
|
|
assert (sumt == hist->total_time);
|
|
|
|
|
assert (sumb == hist->total_bytes);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2001-12-06 05:45:27 -05:00
|
|
|
|
#define APPEND_LITERAL(s) do { \
|
|
|
|
|
memcpy (p, s, sizeof (s) - 1); \
|
|
|
|
|
p += sizeof (s) - 1; \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
#ifndef MAX
|
|
|
|
|
# define MAX(a, b) ((a) >= (b) ? (a) : (b))
|
|
|
|
|
#endif
|
|
|
|
|
|
2001-11-22 23:59:52 -05:00
|
|
|
|
static void
|
2003-09-15 17:14:15 -04:00
|
|
|
|
create_image (struct bar_progress *bp, double dl_total_time)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
|
|
|
|
char *p = bp->buffer;
|
|
|
|
|
long size = bp->initial_length + bp->count;
|
|
|
|
|
|
2001-12-06 05:45:27 -05:00
|
|
|
|
char *size_legible = legible (size);
|
|
|
|
|
int size_legible_len = strlen (size_legible);
|
|
|
|
|
|
2002-04-10 10:10:03 -04:00
|
|
|
|
struct bar_progress_hist *hist = &bp->hist;
|
2002-04-09 18:10:01 -04:00
|
|
|
|
|
2001-11-22 23:59:52 -05:00
|
|
|
|
/* The progress bar should look like this:
|
2001-12-06 05:48:09 -05:00
|
|
|
|
xx% [=======> ] nn,nnn 12.34K/s ETA 00:00
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2001-12-06 05:45:27 -05:00
|
|
|
|
Calculate the geometry. The idea is to assign as much room as
|
|
|
|
|
possible to the progress bar. The other idea is to never let
|
|
|
|
|
things "jitter", i.e. pad elements that vary in size so that
|
|
|
|
|
their variance does not affect the placement of other elements.
|
|
|
|
|
It would be especially bad for the progress bar to be resized
|
|
|
|
|
randomly.
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2001-12-06 05:45:27 -05:00
|
|
|
|
"xx% " or "100%" - percentage - 4 chars
|
|
|
|
|
"[]" - progress bar decorations - 2 chars
|
|
|
|
|
" nnn,nnn,nnn" - downloaded bytes - 12 chars or very rarely more
|
|
|
|
|
" 1012.56K/s" - dl rate - 11 chars
|
|
|
|
|
" ETA xx:xx:xx" - ETA - 13 chars
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2001-12-06 05:45:27 -05:00
|
|
|
|
"=====>..." - progress bar - the rest
|
2001-11-22 23:59:52 -05:00
|
|
|
|
*/
|
2001-12-06 05:45:27 -05:00
|
|
|
|
int dlbytes_size = 1 + MAX (size_legible_len, 11);
|
|
|
|
|
int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2001-11-24 23:46:26 -05:00
|
|
|
|
if (progress_size < 5)
|
|
|
|
|
progress_size = 0;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2001-11-25 16:03:30 -05:00
|
|
|
|
/* "xx% " */
|
2001-11-22 23:59:52 -05:00
|
|
|
|
if (bp->total_length > 0)
|
|
|
|
|
{
|
|
|
|
|
int percentage = (int)(100.0 * size / bp->total_length);
|
|
|
|
|
|
|
|
|
|
assert (percentage <= 100);
|
|
|
|
|
|
2001-11-24 23:46:26 -05:00
|
|
|
|
if (percentage < 100)
|
|
|
|
|
sprintf (p, "%2d%% ", percentage);
|
|
|
|
|
else
|
|
|
|
|
strcpy (p, "100%");
|
|
|
|
|
p += 4;
|
|
|
|
|
}
|
|
|
|
|
else
|
2001-12-06 05:45:27 -05:00
|
|
|
|
APPEND_LITERAL (" ");
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2003-09-23 16:48:10 -04:00
|
|
|
|
/* The progress bar: "[====> ]" or "[++==> ]". */
|
2001-11-24 23:46:26 -05:00
|
|
|
|
if (progress_size && bp->total_length > 0)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
2003-09-22 13:14:19 -04:00
|
|
|
|
/* Size of the initial portion. */
|
|
|
|
|
int insz = (double)bp->initial_length / bp->total_length * progress_size;
|
|
|
|
|
|
|
|
|
|
/* Size of the downloaded portion. */
|
|
|
|
|
int dlsz = (double)size / bp->total_length * progress_size;
|
|
|
|
|
|
2001-11-22 23:59:52 -05:00
|
|
|
|
char *begin;
|
2003-09-22 13:14:19 -04:00
|
|
|
|
int i;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2001-11-24 23:46:26 -05:00
|
|
|
|
assert (dlsz <= progress_size);
|
2003-09-22 13:14:19 -04:00
|
|
|
|
assert (insz <= dlsz);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2001-11-24 23:46:26 -05:00
|
|
|
|
*p++ = '[';
|
2001-11-22 23:59:52 -05:00
|
|
|
|
begin = p;
|
|
|
|
|
|
2003-09-23 16:48:10 -04:00
|
|
|
|
/* Print the initial portion of the download with '+' chars, the
|
2003-09-22 13:14:19 -04:00
|
|
|
|
rest with '=' and one '>'. */
|
|
|
|
|
for (i = 0; i < insz; i++)
|
2003-09-23 16:48:10 -04:00
|
|
|
|
*p++ = '+';
|
2003-09-22 13:14:19 -04:00
|
|
|
|
dlsz -= insz;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
if (dlsz > 0)
|
|
|
|
|
{
|
2003-09-22 13:14:19 -04:00
|
|
|
|
for (i = 0; i < dlsz - 1; i++)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
*p++ = '=';
|
|
|
|
|
*p++ = '>';
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-24 23:46:26 -05:00
|
|
|
|
while (p - begin < progress_size)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
*p++ = ' ';
|
2001-11-24 23:46:26 -05:00
|
|
|
|
*p++ = ']';
|
|
|
|
|
}
|
|
|
|
|
else if (progress_size)
|
|
|
|
|
{
|
|
|
|
|
/* If we can't draw a real progress bar, then at least show
|
|
|
|
|
*something* to the user. */
|
|
|
|
|
int ind = bp->tick % (progress_size * 2 - 6);
|
|
|
|
|
int i, pos;
|
|
|
|
|
|
|
|
|
|
/* Make the star move in two directions. */
|
|
|
|
|
if (ind < progress_size - 2)
|
|
|
|
|
pos = ind + 1;
|
|
|
|
|
else
|
|
|
|
|
pos = progress_size - (ind - progress_size + 5);
|
|
|
|
|
|
|
|
|
|
*p++ = '[';
|
|
|
|
|
for (i = 0; i < progress_size; i++)
|
|
|
|
|
{
|
|
|
|
|
if (i == pos - 1) *p++ = '<';
|
|
|
|
|
else if (i == pos ) *p++ = '=';
|
|
|
|
|
else if (i == pos + 1) *p++ = '>';
|
|
|
|
|
else
|
|
|
|
|
*p++ = ' ';
|
|
|
|
|
}
|
|
|
|
|
*p++ = ']';
|
|
|
|
|
|
|
|
|
|
++bp->tick;
|
2001-11-22 23:59:52 -05:00
|
|
|
|
}
|
|
|
|
|
|
2001-12-06 05:45:27 -05:00
|
|
|
|
/* " 234,567,890" */
|
|
|
|
|
sprintf (p, " %-11s", legible (size));
|
2001-11-25 16:03:30 -05:00
|
|
|
|
p += strlen (p);
|
|
|
|
|
|
|
|
|
|
/* " 1012.45K/s" */
|
2003-09-15 17:14:15 -04:00
|
|
|
|
if (hist->total_time && hist->total_bytes)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
2001-11-24 23:46:26 -05:00
|
|
|
|
static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
|
|
|
|
|
int units = 0;
|
2003-09-23 14:39:22 -04:00
|
|
|
|
/* Calculate the download speed using the history ring and
|
|
|
|
|
recent data that hasn't made it to the ring yet. */
|
|
|
|
|
long dlquant = hist->total_bytes + bp->recent_bytes;
|
|
|
|
|
double dltime = hist->total_time + (dl_total_time - bp->recent_start);
|
|
|
|
|
double dlspeed = calc_rate (dlquant, dltime, &units);
|
|
|
|
|
sprintf (p, " %7.2f%s", dlspeed, short_units[units]);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
p += strlen (p);
|
|
|
|
|
}
|
|
|
|
|
else
|
2001-12-06 05:45:27 -05:00
|
|
|
|
APPEND_LITERAL (" --.--K/s");
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2003-09-22 13:14:19 -04:00
|
|
|
|
/* " ETA xx:xx:xx"; wait for three seconds before displaying the ETA.
|
|
|
|
|
That's because the ETA value needs a while to become
|
|
|
|
|
reliable. */
|
2002-04-10 10:10:03 -04:00
|
|
|
|
if (bp->total_length > 0 && dl_total_time > 3000)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
2002-04-09 18:10:01 -04:00
|
|
|
|
long eta;
|
|
|
|
|
int eta_hrs, eta_min, eta_sec;
|
|
|
|
|
|
|
|
|
|
/* Don't change the value of ETA more than approximately once
|
|
|
|
|
per second; doing so would cause flashing without providing
|
2002-04-10 10:10:03 -04:00
|
|
|
|
any value to the user. */
|
2003-09-23 16:48:10 -04:00
|
|
|
|
if (bp->total_length != size
|
|
|
|
|
&& bp->last_eta_value != 0
|
|
|
|
|
&& dl_total_time - bp->last_eta_time < 900)
|
2002-04-09 18:10:01 -04:00
|
|
|
|
eta = bp->last_eta_value;
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-04-10 10:10:03 -04:00
|
|
|
|
/* Calculate ETA using the average download speed to predict
|
2003-09-15 17:14:15 -04:00
|
|
|
|
the future speed. If you want to use a speed averaged
|
|
|
|
|
over a more recent period, replace dl_total_time with
|
|
|
|
|
hist->total_time and bp->count with hist->total_bytes.
|
|
|
|
|
I found that doing that results in a very jerky and
|
|
|
|
|
ultimately unreliable ETA. */
|
2002-04-10 10:10:03 -04:00
|
|
|
|
double time_sofar = (double)dl_total_time / 1000;
|
2002-04-09 18:10:01 -04:00
|
|
|
|
long bytes_remaining = bp->total_length - size;
|
2002-04-10 10:10:03 -04:00
|
|
|
|
eta = (long) (time_sofar * bytes_remaining / bp->count);
|
2002-04-09 18:10:01 -04:00
|
|
|
|
bp->last_eta_value = eta;
|
|
|
|
|
bp->last_eta_time = dl_total_time;
|
|
|
|
|
}
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
|
|
|
|
eta_hrs = eta / 3600, eta %= 3600;
|
|
|
|
|
eta_min = eta / 60, eta %= 60;
|
|
|
|
|
eta_sec = eta;
|
|
|
|
|
|
|
|
|
|
if (eta_hrs > 99)
|
2002-04-10 10:10:03 -04:00
|
|
|
|
goto no_eta;
|
|
|
|
|
|
|
|
|
|
if (eta_hrs == 0)
|
|
|
|
|
{
|
|
|
|
|
/* Hours not printed: pad with three spaces. */
|
|
|
|
|
APPEND_LITERAL (" ");
|
|
|
|
|
sprintf (p, " ETA %02d:%02d", eta_min, eta_sec);
|
|
|
|
|
}
|
2001-11-22 23:59:52 -05:00
|
|
|
|
else
|
2002-04-10 10:10:03 -04:00
|
|
|
|
{
|
|
|
|
|
if (eta_hrs < 10)
|
|
|
|
|
/* Hours printed with one digit: pad with one space. */
|
|
|
|
|
*p++ = ' ';
|
|
|
|
|
sprintf (p, " ETA %d:%02d:%02d", eta_hrs, eta_min, eta_sec);
|
|
|
|
|
}
|
2001-11-22 23:59:52 -05:00
|
|
|
|
p += strlen (p);
|
|
|
|
|
}
|
|
|
|
|
else if (bp->total_length > 0)
|
2002-04-10 10:10:03 -04:00
|
|
|
|
{
|
|
|
|
|
no_eta:
|
|
|
|
|
APPEND_LITERAL (" ");
|
|
|
|
|
}
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2001-12-06 05:45:27 -05:00
|
|
|
|
assert (p - bp->buffer <= bp->width);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
|
2001-12-06 05:45:27 -05:00
|
|
|
|
while (p < bp->buffer + bp->width)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
*p++ = ' ';
|
|
|
|
|
*p = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-23 11:12:53 -05:00
|
|
|
|
/* Print the contents of the buffer as a one-line ASCII "image" so
|
|
|
|
|
that it can be overwritten next time. */
|
|
|
|
|
|
2001-11-22 23:59:52 -05:00
|
|
|
|
static void
|
|
|
|
|
display_image (char *buf)
|
|
|
|
|
{
|
2001-12-10 00:31:45 -05:00
|
|
|
|
int old = log_set_save_context (0);
|
2001-11-30 03:02:33 -05:00
|
|
|
|
logputs (LOG_VERBOSE, "\r");
|
2001-11-24 23:46:26 -05:00
|
|
|
|
logputs (LOG_VERBOSE, buf);
|
2001-12-10 00:31:45 -05:00
|
|
|
|
log_set_save_context (old);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2001-11-23 11:24:06 -05:00
|
|
|
|
bar_set_params (const char *params)
|
2001-11-22 23:59:52 -05:00
|
|
|
|
{
|
2001-11-23 11:12:53 -05:00
|
|
|
|
int sw;
|
2002-05-24 10:53:18 -04:00
|
|
|
|
char *term = getenv ("TERM");
|
2001-11-23 11:12:53 -05:00
|
|
|
|
|
2001-12-06 02:14:35 -05:00
|
|
|
|
if (params
|
|
|
|
|
&& 0 == strcmp (params, "force"))
|
|
|
|
|
current_impl_locked = 1;
|
|
|
|
|
|
2001-11-23 11:24:06 -05:00
|
|
|
|
if ((opt.lfilename
|
2001-11-23 11:12:53 -05:00
|
|
|
|
#ifdef HAVE_ISATTY
|
2002-05-24 10:53:18 -04:00
|
|
|
|
/* The progress bar doesn't make sense if the output is not a
|
|
|
|
|
TTY -- when logging to file, it is better to review the
|
|
|
|
|
dots. */
|
2001-11-23 11:24:06 -05:00
|
|
|
|
|| !isatty (fileno (stderr))
|
2001-11-23 11:12:53 -05:00
|
|
|
|
#else
|
2001-11-23 11:24:06 -05:00
|
|
|
|
1
|
2001-11-23 11:12:53 -05:00
|
|
|
|
#endif
|
2002-05-24 10:53:18 -04:00
|
|
|
|
/* Normally we don't depend on terminal type because the
|
|
|
|
|
progress bar only uses ^M to move the cursor to the
|
|
|
|
|
beginning of line, which works even on dumb terminals. But
|
|
|
|
|
Jamie Zawinski reports that ^M and ^H tricks don't work in
|
|
|
|
|
Emacs shell buffers, and only make a mess. */
|
|
|
|
|
|| (term && 0 == strcmp (term, "emacs"))
|
2001-11-23 11:24:06 -05:00
|
|
|
|
)
|
2001-12-06 02:14:35 -05:00
|
|
|
|
&& !current_impl_locked)
|
2001-11-23 11:12:53 -05:00
|
|
|
|
{
|
2001-11-23 11:24:06 -05:00
|
|
|
|
/* We're not printing to a TTY, so revert to the fallback
|
|
|
|
|
display. #### We're recursively calling
|
|
|
|
|
set_progress_implementation here, which is slightly kludgy.
|
2001-11-27 05:29:45 -05:00
|
|
|
|
It would be nicer if we provided that function a return value
|
|
|
|
|
indicating a failure of some sort. */
|
|
|
|
|
set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);
|
2001-11-23 11:12:53 -05:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sw = determine_screen_width ();
|
2001-11-22 23:59:52 -05:00
|
|
|
|
if (sw && sw >= MINIMUM_SCREEN_WIDTH)
|
|
|
|
|
screen_width = sw;
|
|
|
|
|
}
|
|
|
|
|
|
2001-12-06 05:45:27 -05:00
|
|
|
|
#ifdef SIGWINCH
|
2001-11-22 23:59:52 -05:00
|
|
|
|
RETSIGTYPE
|
|
|
|
|
progress_handle_sigwinch (int sig)
|
|
|
|
|
{
|
|
|
|
|
int sw = determine_screen_width ();
|
|
|
|
|
if (sw && sw >= MINIMUM_SCREEN_WIDTH)
|
|
|
|
|
screen_width = sw;
|
2001-12-06 05:45:27 -05:00
|
|
|
|
signal (SIGWINCH, progress_handle_sigwinch);
|
2001-11-22 23:59:52 -05:00
|
|
|
|
}
|
2001-12-06 05:45:27 -05:00
|
|
|
|
#endif
|