curl/src/tool_progress.c
Daniel Stenberg f2daef6ad4
tool_progress: remove 'Qd' from the parallel progress bar
The "queued" value is no longer showing anything useful to the user. It
is an internal number of transfers waiting at that moment.

Closes #9389
2022-08-31 15:58:15 +02:00

332 lines
10 KiB
C

/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "tool_setup.h"
#include "tool_operate.h"
#include "tool_progress.h"
#include "tool_util.h"
#define ENABLE_CURLX_PRINTF
/* use our own printf() functions */
#include "curlx.h"
/* The point of this function would be to return a string of the input data,
but never longer than 5 columns (+ one zero byte).
Add suffix k, M, G when suitable... */
static char *max5data(curl_off_t bytes, char *max5)
{
#define ONE_KILOBYTE CURL_OFF_T_C(1024)
#define ONE_MEGABYTE (CURL_OFF_T_C(1024) * ONE_KILOBYTE)
#define ONE_GIGABYTE (CURL_OFF_T_C(1024) * ONE_MEGABYTE)
#define ONE_TERABYTE (CURL_OFF_T_C(1024) * ONE_GIGABYTE)
#define ONE_PETABYTE (CURL_OFF_T_C(1024) * ONE_TERABYTE)
if(bytes < CURL_OFF_T_C(100000))
msnprintf(max5, 6, "%5" CURL_FORMAT_CURL_OFF_T, bytes);
else if(bytes < CURL_OFF_T_C(10000) * ONE_KILOBYTE)
msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "k", bytes/ONE_KILOBYTE);
else if(bytes < CURL_OFF_T_C(100) * ONE_MEGABYTE)
/* 'XX.XM' is good as long as we're less than 100 megs */
msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE,
(bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) );
#if (SIZEOF_CURL_OFF_T > 4)
else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE)
/* 'XXXXM' is good until we're at 10000MB or above */
msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
else if(bytes < CURL_OFF_T_C(100) * ONE_GIGABYTE)
/* 10000 MB - 100 GB, we show it as XX.XG */
msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE,
(bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) );
else if(bytes < CURL_OFF_T_C(10000) * ONE_GIGABYTE)
/* up to 10000GB, display without decimal: XXXXG */
msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE);
else if(bytes < CURL_OFF_T_C(10000) * ONE_TERABYTE)
/* up to 10000TB, display without decimal: XXXXT */
msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "T", bytes/ONE_TERABYTE);
else
/* up to 10000PB, display without decimal: XXXXP */
msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE);
/* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number
can hold, but our data type is signed so 8192PB will be the maximum. */
#else
else
msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
#endif
return max5;
}
int xferinfo_cb(void *clientp,
curl_off_t dltotal,
curl_off_t dlnow,
curl_off_t ultotal,
curl_off_t ulnow)
{
struct per_transfer *per = clientp;
struct OperationConfig *config = per->config;
per->dltotal = dltotal;
per->dlnow = dlnow;
per->ultotal = ultotal;
per->ulnow = ulnow;
if(per->abort)
return 1;
if(config->readbusy) {
config->readbusy = FALSE;
curl_easy_pause(per->curl, CURLPAUSE_CONT);
}
return 0;
}
/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero
byte) */
static void time2str(char *r, curl_off_t seconds)
{
curl_off_t h;
if(seconds <= 0) {
strcpy(r, "--:--:--");
return;
}
h = seconds / CURL_OFF_T_C(3600);
if(h <= CURL_OFF_T_C(99)) {
curl_off_t m = (seconds - (h*CURL_OFF_T_C(3600))) / CURL_OFF_T_C(60);
curl_off_t s = (seconds - (h*CURL_OFF_T_C(3600))) - (m*CURL_OFF_T_C(60));
msnprintf(r, 9, "%2" CURL_FORMAT_CURL_OFF_T ":%02" CURL_FORMAT_CURL_OFF_T
":%02" CURL_FORMAT_CURL_OFF_T, h, m, s);
}
else {
/* this equals to more than 99 hours, switch to a more suitable output
format to fit within the limits. */
curl_off_t d = seconds / CURL_OFF_T_C(86400);
h = (seconds - (d*CURL_OFF_T_C(86400))) / CURL_OFF_T_C(3600);
if(d <= CURL_OFF_T_C(999))
msnprintf(r, 9, "%3" CURL_FORMAT_CURL_OFF_T
"d %02" CURL_FORMAT_CURL_OFF_T "h", d, h);
else
msnprintf(r, 9, "%7" CURL_FORMAT_CURL_OFF_T "d", d);
}
}
static curl_off_t all_dltotal = 0;
static curl_off_t all_ultotal = 0;
static curl_off_t all_dlalready = 0;
static curl_off_t all_ulalready = 0;
curl_off_t all_xfers = 0; /* current total */
struct speedcount {
curl_off_t dl;
curl_off_t ul;
struct timeval stamp;
};
#define SPEEDCNT 10
static unsigned int speedindex;
static bool indexwrapped;
static struct speedcount speedstore[SPEEDCNT];
/*
|DL% UL% Dled Uled Xfers Live Total Current Left Speed
| 6 -- 9.9G 0 2 2 0:00:40 0:00:02 0:00:37 4087M
*/
bool progress_meter(struct GlobalConfig *global,
struct timeval *start,
bool final)
{
static struct timeval stamp;
static bool header = FALSE;
struct timeval now;
long diff;
if(global->noprogress)
return FALSE;
now = tvnow();
diff = tvdiff(now, stamp);
if(!header) {
header = TRUE;
fputs("DL% UL% Dled Uled Xfers Live "
"Total Current Left Speed\n",
global->errors);
}
if(final || (diff > 500)) {
char time_left[10];
char time_total[10];
char time_spent[10];
char buffer[3][6];
curl_off_t spent = tvdiff(now, *start)/1000;
char dlpercen[4]="--";
char ulpercen[4]="--";
struct per_transfer *per;
curl_off_t all_dlnow = 0;
curl_off_t all_ulnow = 0;
bool dlknown = TRUE;
bool ulknown = TRUE;
curl_off_t all_running = 0; /* in progress */
curl_off_t speed = 0;
unsigned int i;
stamp = now;
/* first add the amounts of the already completed transfers */
all_dlnow += all_dlalready;
all_ulnow += all_ulalready;
for(per = transfers; per; per = per->next) {
all_dlnow += per->dlnow;
all_ulnow += per->ulnow;
if(!per->dltotal)
dlknown = FALSE;
else if(!per->dltotal_added) {
/* only add this amount once */
all_dltotal += per->dltotal;
per->dltotal_added = TRUE;
}
if(!per->ultotal)
ulknown = FALSE;
else if(!per->ultotal_added) {
/* only add this amount once */
all_ultotal += per->ultotal;
per->ultotal_added = TRUE;
}
if(per->added)
all_running++;
}
if(dlknown && all_dltotal)
/* TODO: handle integer overflow */
msnprintf(dlpercen, sizeof(dlpercen), "%3" CURL_FORMAT_CURL_OFF_T,
all_dlnow * 100 / all_dltotal);
if(ulknown && all_ultotal)
/* TODO: handle integer overflow */
msnprintf(ulpercen, sizeof(ulpercen), "%3" CURL_FORMAT_CURL_OFF_T,
all_ulnow * 100 / all_ultotal);
/* get the transfer speed, the higher of the two */
i = speedindex;
speedstore[i].dl = all_dlnow;
speedstore[i].ul = all_ulnow;
speedstore[i].stamp = now;
if(++speedindex >= SPEEDCNT) {
indexwrapped = TRUE;
speedindex = 0;
}
{
long deltams;
curl_off_t dl;
curl_off_t ul;
curl_off_t dls;
curl_off_t uls;
if(indexwrapped) {
/* 'speedindex' is the oldest stored data */
deltams = tvdiff(now, speedstore[speedindex].stamp);
dl = all_dlnow - speedstore[speedindex].dl;
ul = all_ulnow - speedstore[speedindex].ul;
}
else {
/* since the beginning */
deltams = tvdiff(now, *start);
dl = all_dlnow;
ul = all_ulnow;
}
if(!deltams) /* no division by zero please */
deltams++;
dls = (curl_off_t)((double)dl / ((double)deltams/1000.0));
uls = (curl_off_t)((double)ul / ((double)deltams/1000.0));
speed = dls > uls ? dls : uls;
}
if(dlknown && speed) {
curl_off_t est = all_dltotal / speed;
curl_off_t left = (all_dltotal - all_dlnow) / speed;
time2str(time_left, left);
time2str(time_total, est);
}
else {
time2str(time_left, 0);
time2str(time_total, 0);
}
time2str(time_spent, spent);
fprintf(global->errors,
"\r"
"%-3s " /* percent downloaded */
"%-3s " /* percent uploaded */
"%s " /* Dled */
"%s " /* Uled */
"%5" CURL_FORMAT_CURL_OFF_T " " /* Xfers */
"%5" CURL_FORMAT_CURL_OFF_T " " /* Live */
" %s " /* Total time */
"%s " /* Current time */
"%s " /* Time left */
"%s " /* Speed */
"%5s" /* final newline */,
dlpercen, /* 3 letters */
ulpercen, /* 3 letters */
max5data(all_dlnow, buffer[0]),
max5data(all_ulnow, buffer[1]),
all_xfers,
all_running,
time_total,
time_spent,
time_left,
max5data(speed, buffer[2]), /* speed */
final ? "\n" :"");
return TRUE;
}
return FALSE;
}
void progress_finalize(struct per_transfer *per)
{
/* get the numbers before this transfer goes away */
all_dlalready += per->dlnow;
all_ulalready += per->ulnow;
if(!per->dltotal_added) {
all_dltotal += per->dltotal;
per->dltotal_added = TRUE;
}
if(!per->ultotal_added) {
all_ultotal += per->ultotal;
per->ultotal_added = TRUE;
}
}