#include "tool_setup.h"
#include "tool_operate.h"
#include "tool_progress.h"
#include "tool_util.h"
static char *max5data(curl_off_t bytes, char *max5)
{
const char unit[] = { 'k', 'M', 'G', 'T', 'P', 0 };
int k = 0;
if(bytes < 100000) {
curl_msnprintf(max5, 6, "%5" CURL_FORMAT_CURL_OFF_T, bytes);
return max5;
}
do {
curl_off_t nbytes = bytes / 1024;
if(nbytes < 100) {
curl_msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
CURL_FORMAT_CURL_OFF_T "%c", bytes/1024,
(bytes%1024) / (1024/10), unit[k]);
break;
}
else if(nbytes < 10000) {
curl_msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "%c", nbytes,
unit[k]);
break;
}
bytes = nbytes;
k++;
DEBUGASSERT(unit[k]);
} while(unit[k]);
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;
}
static void time2str(char *r, curl_off_t seconds)
{
curl_off_t h;
if(seconds <= 0) {
strcpy(r, "--:--:--");
return;
}
h = seconds / 3600;
if(h <= 99) {
curl_off_t m = (seconds - (h * 3600)) / 60;
curl_off_t s = (seconds - (h * 3600)) - (m * 60);
curl_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 {
curl_off_t d = seconds / 86400;
h = (seconds - (d * 86400)) / 3600;
if(d <= 999)
curl_msnprintf(r, 9, "%3" CURL_FORMAT_CURL_OFF_T
"d %02" CURL_FORMAT_CURL_OFF_T "h", d, h);
else
curl_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;
struct speedcount {
curl_off_t dl;
curl_off_t ul;
struct curltime stamp;
};
#define SPEEDCNT 10
static unsigned int speedindex;
static bool indexwrapped;
static struct speedcount speedstore[SPEEDCNT];
static void add_offt(curl_off_t *val, curl_off_t add)
{
if(CURL_OFF_T_MAX - *val < add)
*val = CURL_OFF_T_MAX;
else
*val += add;
}
bool progress_meter(CURLM *multi,
struct curltime *start,
bool final)
{
static struct curltime stamp;
static bool header = FALSE;
struct curltime now;
timediff_t diff;
if(global->noprogress || global->silent)
return FALSE;
now = curlx_now();
diff = curlx_timediff(now, stamp);
if(!header) {
header = TRUE;
fputs("DL% UL% Dled Uled Xfers Live "
"Total Current Left Speed\n",
tool_stderr);
}
if(final || (diff > 500)) {
char time_left[10];
char time_total[10];
char time_spent[10];
char buffer[3][6];
curl_off_t spent = curlx_timediff(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;
curl_off_t xfers_added = 0;
curl_off_t xfers_running = 0;
bool dlknown = TRUE;
bool ulknown = TRUE;
curl_off_t speed = 0;
unsigned int i;
stamp = now;
add_offt(&all_dlnow, all_dlalready);
add_offt(&all_ulnow, all_ulalready);
for(per = transfers; per; per = per->next) {
add_offt(&all_dlnow, per->dlnow);
add_offt(&all_ulnow, per->ulnow);
if(!per->dltotal)
dlknown = FALSE;
else if(!per->dltotal_added) {
add_offt(&all_dltotal, per->dltotal);
per->dltotal_added = TRUE;
}
if(!per->ultotal)
ulknown = FALSE;
else if(!per->ultotal_added) {
add_offt(&all_ultotal, per->ultotal);
per->ultotal_added = TRUE;
}
}
if(dlknown && all_dltotal)
curl_msnprintf(dlpercen, sizeof(dlpercen), "%3" CURL_FORMAT_CURL_OFF_T,
all_dlnow < (CURL_OFF_T_MAX/100) ?
(all_dlnow * 100 / all_dltotal) :
(all_dlnow / (all_dltotal/100)));
if(ulknown && all_ultotal)
curl_msnprintf(ulpercen, sizeof(ulpercen), "%3" CURL_FORMAT_CURL_OFF_T,
all_ulnow < (CURL_OFF_T_MAX/100) ?
(all_ulnow * 100 / all_ultotal) :
(all_ulnow / (all_ultotal/100)));
i = speedindex;
speedstore[i].dl = all_dlnow;
speedstore[i].ul = all_ulnow;
speedstore[i].stamp = now;
if(++speedindex >= SPEEDCNT) {
indexwrapped = TRUE;
speedindex = 0;
}
{
timediff_t deltams;
curl_off_t dl;
curl_off_t ul;
curl_off_t dls;
curl_off_t uls;
if(indexwrapped) {
deltams = curlx_timediff(now, speedstore[speedindex].stamp);
dl = all_dlnow - speedstore[speedindex].dl;
ul = all_ulnow - speedstore[speedindex].ul;
}
else {
deltams = curlx_timediff(now, *start);
dl = all_dlnow;
ul = all_ulnow;
}
if(!deltams)
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);
(void)curl_multi_get_offt(multi, CURLMINFO_XFERS_ADDED, &xfers_added);
(void)curl_multi_get_offt(multi, CURLMINFO_XFERS_RUNNING, &xfers_running);
curl_mfprintf(tool_stderr,
"\r"
"%-3s "
"%-3s "
"%s "
"%s "
"%5" CURL_FORMAT_CURL_OFF_T " "
"%5" CURL_FORMAT_CURL_OFF_T " "
" %s "
"%s "
"%s "
"%s "
"%5s" ,
dlpercen,
ulpercen,
max5data(all_dlnow, buffer[0]),
max5data(all_ulnow, buffer[1]),
xfers_added,
xfers_running,
time_total,
time_spent,
time_left,
max5data(speed, buffer[2]),
final ? "\n" :"");
return TRUE;
}
return FALSE;
}
void progress_finalize(struct per_transfer *per)
{
add_offt(&all_dlalready, per->dlnow);
add_offt(&all_ulalready, per->ulnow);
if(!per->dltotal_added) {
add_offt(&all_dltotal, per->dltotal);
per->dltotal_added = TRUE;
}
if(!per->ultotal_added) {
add_offt(&all_ultotal, per->ultotal);
per->ultotal_added = TRUE;
}
}