xref: /third_party/curl/lib/progress.c (revision 13498266)
113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#include "urldata.h"
2813498266Sopenharmony_ci#include "sendf.h"
2913498266Sopenharmony_ci#include "multiif.h"
3013498266Sopenharmony_ci#include "progress.h"
3113498266Sopenharmony_ci#include "timeval.h"
3213498266Sopenharmony_ci#include "curl_printf.h"
3313498266Sopenharmony_ci
3413498266Sopenharmony_ci/* check rate limits within this many recent milliseconds, at minimum. */
3513498266Sopenharmony_ci#define MIN_RATE_LIMIT_PERIOD 3000
3613498266Sopenharmony_ci
3713498266Sopenharmony_ci#ifndef CURL_DISABLE_PROGRESS_METER
3813498266Sopenharmony_ci/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero
3913498266Sopenharmony_ci   byte) */
4013498266Sopenharmony_cistatic void time2str(char *r, curl_off_t seconds)
4113498266Sopenharmony_ci{
4213498266Sopenharmony_ci  curl_off_t h;
4313498266Sopenharmony_ci  if(seconds <= 0) {
4413498266Sopenharmony_ci    strcpy(r, "--:--:--");
4513498266Sopenharmony_ci    return;
4613498266Sopenharmony_ci  }
4713498266Sopenharmony_ci  h = seconds / CURL_OFF_T_C(3600);
4813498266Sopenharmony_ci  if(h <= CURL_OFF_T_C(99)) {
4913498266Sopenharmony_ci    curl_off_t m = (seconds - (h*CURL_OFF_T_C(3600))) / CURL_OFF_T_C(60);
5013498266Sopenharmony_ci    curl_off_t s = (seconds - (h*CURL_OFF_T_C(3600))) - (m*CURL_OFF_T_C(60));
5113498266Sopenharmony_ci    msnprintf(r, 9, "%2" CURL_FORMAT_CURL_OFF_T ":%02" CURL_FORMAT_CURL_OFF_T
5213498266Sopenharmony_ci              ":%02" CURL_FORMAT_CURL_OFF_T, h, m, s);
5313498266Sopenharmony_ci  }
5413498266Sopenharmony_ci  else {
5513498266Sopenharmony_ci    /* this equals to more than 99 hours, switch to a more suitable output
5613498266Sopenharmony_ci       format to fit within the limits. */
5713498266Sopenharmony_ci    curl_off_t d = seconds / CURL_OFF_T_C(86400);
5813498266Sopenharmony_ci    h = (seconds - (d*CURL_OFF_T_C(86400))) / CURL_OFF_T_C(3600);
5913498266Sopenharmony_ci    if(d <= CURL_OFF_T_C(999))
6013498266Sopenharmony_ci      msnprintf(r, 9, "%3" CURL_FORMAT_CURL_OFF_T
6113498266Sopenharmony_ci                "d %02" CURL_FORMAT_CURL_OFF_T "h", d, h);
6213498266Sopenharmony_ci    else
6313498266Sopenharmony_ci      msnprintf(r, 9, "%7" CURL_FORMAT_CURL_OFF_T "d", d);
6413498266Sopenharmony_ci  }
6513498266Sopenharmony_ci}
6613498266Sopenharmony_ci
6713498266Sopenharmony_ci/* The point of this function would be to return a string of the input data,
6813498266Sopenharmony_ci   but never longer than 5 columns (+ one zero byte).
6913498266Sopenharmony_ci   Add suffix k, M, G when suitable... */
7013498266Sopenharmony_cistatic char *max5data(curl_off_t bytes, char *max5)
7113498266Sopenharmony_ci{
7213498266Sopenharmony_ci#define ONE_KILOBYTE  CURL_OFF_T_C(1024)
7313498266Sopenharmony_ci#define ONE_MEGABYTE (CURL_OFF_T_C(1024) * ONE_KILOBYTE)
7413498266Sopenharmony_ci#define ONE_GIGABYTE (CURL_OFF_T_C(1024) * ONE_MEGABYTE)
7513498266Sopenharmony_ci#define ONE_TERABYTE (CURL_OFF_T_C(1024) * ONE_GIGABYTE)
7613498266Sopenharmony_ci#define ONE_PETABYTE (CURL_OFF_T_C(1024) * ONE_TERABYTE)
7713498266Sopenharmony_ci
7813498266Sopenharmony_ci  if(bytes < CURL_OFF_T_C(100000))
7913498266Sopenharmony_ci    msnprintf(max5, 6, "%5" CURL_FORMAT_CURL_OFF_T, bytes);
8013498266Sopenharmony_ci
8113498266Sopenharmony_ci  else if(bytes < CURL_OFF_T_C(10000) * ONE_KILOBYTE)
8213498266Sopenharmony_ci    msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "k", bytes/ONE_KILOBYTE);
8313498266Sopenharmony_ci
8413498266Sopenharmony_ci  else if(bytes < CURL_OFF_T_C(100) * ONE_MEGABYTE)
8513498266Sopenharmony_ci    /* 'XX.XM' is good as long as we're less than 100 megs */
8613498266Sopenharmony_ci    msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
8713498266Sopenharmony_ci              CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE,
8813498266Sopenharmony_ci              (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) );
8913498266Sopenharmony_ci
9013498266Sopenharmony_ci  else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE)
9113498266Sopenharmony_ci    /* 'XXXXM' is good until we're at 10000MB or above */
9213498266Sopenharmony_ci    msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
9313498266Sopenharmony_ci
9413498266Sopenharmony_ci  else if(bytes < CURL_OFF_T_C(100) * ONE_GIGABYTE)
9513498266Sopenharmony_ci    /* 10000 MB - 100 GB, we show it as XX.XG */
9613498266Sopenharmony_ci    msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
9713498266Sopenharmony_ci              CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE,
9813498266Sopenharmony_ci              (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) );
9913498266Sopenharmony_ci
10013498266Sopenharmony_ci  else if(bytes < CURL_OFF_T_C(10000) * ONE_GIGABYTE)
10113498266Sopenharmony_ci    /* up to 10000GB, display without decimal: XXXXG */
10213498266Sopenharmony_ci    msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE);
10313498266Sopenharmony_ci
10413498266Sopenharmony_ci  else if(bytes < CURL_OFF_T_C(10000) * ONE_TERABYTE)
10513498266Sopenharmony_ci    /* up to 10000TB, display without decimal: XXXXT */
10613498266Sopenharmony_ci    msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "T", bytes/ONE_TERABYTE);
10713498266Sopenharmony_ci
10813498266Sopenharmony_ci  else
10913498266Sopenharmony_ci    /* up to 10000PB, display without decimal: XXXXP */
11013498266Sopenharmony_ci    msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE);
11113498266Sopenharmony_ci
11213498266Sopenharmony_ci  /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number can
11313498266Sopenharmony_ci     hold, but our data type is signed so 8192PB will be the maximum. */
11413498266Sopenharmony_ci
11513498266Sopenharmony_ci  return max5;
11613498266Sopenharmony_ci}
11713498266Sopenharmony_ci#endif
11813498266Sopenharmony_ci
11913498266Sopenharmony_ci/*
12013498266Sopenharmony_ci
12113498266Sopenharmony_ci   New proposed interface, 9th of February 2000:
12213498266Sopenharmony_ci
12313498266Sopenharmony_ci   pgrsStartNow() - sets start time
12413498266Sopenharmony_ci   pgrsSetDownloadSize(x) - known expected download size
12513498266Sopenharmony_ci   pgrsSetUploadSize(x) - known expected upload size
12613498266Sopenharmony_ci   pgrsSetDownloadCounter() - amount of data currently downloaded
12713498266Sopenharmony_ci   pgrsSetUploadCounter() - amount of data currently uploaded
12813498266Sopenharmony_ci   pgrsUpdate() - show progress
12913498266Sopenharmony_ci   pgrsDone() - transfer complete
13013498266Sopenharmony_ci
13113498266Sopenharmony_ci*/
13213498266Sopenharmony_ci
13313498266Sopenharmony_ciint Curl_pgrsDone(struct Curl_easy *data)
13413498266Sopenharmony_ci{
13513498266Sopenharmony_ci  int rc;
13613498266Sopenharmony_ci  data->progress.lastshow = 0;
13713498266Sopenharmony_ci  rc = Curl_pgrsUpdate(data); /* the final (forced) update */
13813498266Sopenharmony_ci  if(rc)
13913498266Sopenharmony_ci    return rc;
14013498266Sopenharmony_ci
14113498266Sopenharmony_ci  if(!(data->progress.flags & PGRS_HIDE) &&
14213498266Sopenharmony_ci     !data->progress.callback)
14313498266Sopenharmony_ci    /* only output if we don't use a progress callback and we're not
14413498266Sopenharmony_ci     * hidden */
14513498266Sopenharmony_ci    fprintf(data->set.err, "\n");
14613498266Sopenharmony_ci
14713498266Sopenharmony_ci  data->progress.speeder_c = 0; /* reset the progress meter display */
14813498266Sopenharmony_ci  return 0;
14913498266Sopenharmony_ci}
15013498266Sopenharmony_ci
15113498266Sopenharmony_ci/* reset the known transfer sizes */
15213498266Sopenharmony_civoid Curl_pgrsResetTransferSizes(struct Curl_easy *data)
15313498266Sopenharmony_ci{
15413498266Sopenharmony_ci  Curl_pgrsSetDownloadSize(data, -1);
15513498266Sopenharmony_ci  Curl_pgrsSetUploadSize(data, -1);
15613498266Sopenharmony_ci}
15713498266Sopenharmony_ci
15813498266Sopenharmony_ci/*
15913498266Sopenharmony_ci *
16013498266Sopenharmony_ci * Curl_pgrsTimeWas(). Store the timestamp time at the given label.
16113498266Sopenharmony_ci */
16213498266Sopenharmony_civoid Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
16313498266Sopenharmony_ci                      struct curltime timestamp)
16413498266Sopenharmony_ci{
16513498266Sopenharmony_ci  timediff_t *delta = NULL;
16613498266Sopenharmony_ci
16713498266Sopenharmony_ci  switch(timer) {
16813498266Sopenharmony_ci  default:
16913498266Sopenharmony_ci  case TIMER_NONE:
17013498266Sopenharmony_ci    /* mistake filter */
17113498266Sopenharmony_ci    break;
17213498266Sopenharmony_ci  case TIMER_STARTOP:
17313498266Sopenharmony_ci    /* This is set at the start of a transfer */
17413498266Sopenharmony_ci    data->progress.t_startop = timestamp;
17513498266Sopenharmony_ci    break;
17613498266Sopenharmony_ci  case TIMER_STARTSINGLE:
17713498266Sopenharmony_ci    /* This is set at the start of each single transfer */
17813498266Sopenharmony_ci    data->progress.t_startsingle = timestamp;
17913498266Sopenharmony_ci    data->progress.is_t_startransfer_set = false;
18013498266Sopenharmony_ci    break;
18113498266Sopenharmony_ci  case TIMER_POSTQUEUE:
18213498266Sopenharmony_ci    /* Set when the transfer starts (after potentially having been brought
18313498266Sopenharmony_ci       back from the waiting queue). It needs to count from t_startop and not
18413498266Sopenharmony_ci       t_startsingle since the latter is reset when a connection is brought
18513498266Sopenharmony_ci       back from the pending queue. */
18613498266Sopenharmony_ci    data->progress.t_postqueue =
18713498266Sopenharmony_ci      Curl_timediff_us(timestamp, data->progress.t_startop);
18813498266Sopenharmony_ci    break;
18913498266Sopenharmony_ci  case TIMER_STARTACCEPT:
19013498266Sopenharmony_ci    data->progress.t_acceptdata = timestamp;
19113498266Sopenharmony_ci    break;
19213498266Sopenharmony_ci  case TIMER_NAMELOOKUP:
19313498266Sopenharmony_ci    delta = &data->progress.t_nslookup;
19413498266Sopenharmony_ci    break;
19513498266Sopenharmony_ci  case TIMER_CONNECT:
19613498266Sopenharmony_ci    delta = &data->progress.t_connect;
19713498266Sopenharmony_ci    break;
19813498266Sopenharmony_ci  case TIMER_APPCONNECT:
19913498266Sopenharmony_ci    delta = &data->progress.t_appconnect;
20013498266Sopenharmony_ci    break;
20113498266Sopenharmony_ci  case TIMER_PRETRANSFER:
20213498266Sopenharmony_ci    delta = &data->progress.t_pretransfer;
20313498266Sopenharmony_ci    break;
20413498266Sopenharmony_ci  case TIMER_STARTTRANSFER:
20513498266Sopenharmony_ci    delta = &data->progress.t_starttransfer;
20613498266Sopenharmony_ci    /* prevent updating t_starttransfer unless:
20713498266Sopenharmony_ci     *   1) this is the first time we're setting t_starttransfer
20813498266Sopenharmony_ci     *   2) a redirect has occurred since the last time t_starttransfer was set
20913498266Sopenharmony_ci     * This prevents repeated invocations of the function from incorrectly
21013498266Sopenharmony_ci     * changing the t_starttransfer time.
21113498266Sopenharmony_ci     */
21213498266Sopenharmony_ci    if(data->progress.is_t_startransfer_set) {
21313498266Sopenharmony_ci      return;
21413498266Sopenharmony_ci    }
21513498266Sopenharmony_ci    else {
21613498266Sopenharmony_ci      data->progress.is_t_startransfer_set = true;
21713498266Sopenharmony_ci      break;
21813498266Sopenharmony_ci    }
21913498266Sopenharmony_ci  case TIMER_POSTRANSFER:
22013498266Sopenharmony_ci    /* this is the normal end-of-transfer thing */
22113498266Sopenharmony_ci    break;
22213498266Sopenharmony_ci  case TIMER_REDIRECT:
22313498266Sopenharmony_ci    data->progress.t_redirect = Curl_timediff_us(timestamp,
22413498266Sopenharmony_ci                                                 data->progress.start);
22513498266Sopenharmony_ci    break;
22613498266Sopenharmony_ci  }
22713498266Sopenharmony_ci  if(delta) {
22813498266Sopenharmony_ci    timediff_t us = Curl_timediff_us(timestamp, data->progress.t_startsingle);
22913498266Sopenharmony_ci    if(us < 1)
23013498266Sopenharmony_ci      us = 1; /* make sure at least one microsecond passed */
23113498266Sopenharmony_ci    *delta += us;
23213498266Sopenharmony_ci  }
23313498266Sopenharmony_ci}
23413498266Sopenharmony_ci
23513498266Sopenharmony_ci/*
23613498266Sopenharmony_ci *
23713498266Sopenharmony_ci * Curl_pgrsTime(). Store the current time at the given label. This fetches a
23813498266Sopenharmony_ci * fresh "now" and returns it.
23913498266Sopenharmony_ci *
24013498266Sopenharmony_ci * @unittest: 1399
24113498266Sopenharmony_ci */
24213498266Sopenharmony_cistruct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
24313498266Sopenharmony_ci{
24413498266Sopenharmony_ci  struct curltime now = Curl_now();
24513498266Sopenharmony_ci
24613498266Sopenharmony_ci  Curl_pgrsTimeWas(data, timer, now);
24713498266Sopenharmony_ci  return now;
24813498266Sopenharmony_ci}
24913498266Sopenharmony_ci
25013498266Sopenharmony_civoid Curl_pgrsStartNow(struct Curl_easy *data)
25113498266Sopenharmony_ci{
25213498266Sopenharmony_ci  data->progress.speeder_c = 0; /* reset the progress meter display */
25313498266Sopenharmony_ci  data->progress.start = Curl_now();
25413498266Sopenharmony_ci  data->progress.is_t_startransfer_set = false;
25513498266Sopenharmony_ci  data->progress.ul_limit_start = data->progress.start;
25613498266Sopenharmony_ci  data->progress.dl_limit_start = data->progress.start;
25713498266Sopenharmony_ci  data->progress.ul_limit_size = 0;
25813498266Sopenharmony_ci  data->progress.dl_limit_size = 0;
25913498266Sopenharmony_ci  data->progress.downloaded = 0;
26013498266Sopenharmony_ci  data->progress.uploaded = 0;
26113498266Sopenharmony_ci  /* clear all bits except HIDE and HEADERS_OUT */
26213498266Sopenharmony_ci  data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT;
26313498266Sopenharmony_ci  Curl_ratelimit(data, data->progress.start);
26413498266Sopenharmony_ci}
26513498266Sopenharmony_ci
26613498266Sopenharmony_ci/*
26713498266Sopenharmony_ci * This is used to handle speed limits, calculating how many milliseconds to
26813498266Sopenharmony_ci * wait until we're back under the speed limit, if needed.
26913498266Sopenharmony_ci *
27013498266Sopenharmony_ci * The way it works is by having a "starting point" (time & amount of data
27113498266Sopenharmony_ci * transferred by then) used in the speed computation, to be used instead of
27213498266Sopenharmony_ci * the start of the transfer.  This starting point is regularly moved as
27313498266Sopenharmony_ci * transfer goes on, to keep getting accurate values (instead of average over
27413498266Sopenharmony_ci * the entire transfer).
27513498266Sopenharmony_ci *
27613498266Sopenharmony_ci * This function takes the current amount of data transferred, the amount at
27713498266Sopenharmony_ci * the starting point, the limit (in bytes/s), the time of the starting point
27813498266Sopenharmony_ci * and the current time.
27913498266Sopenharmony_ci *
28013498266Sopenharmony_ci * Returns 0 if no waiting is needed or when no waiting is needed but the
28113498266Sopenharmony_ci * starting point should be reset (to current); or the number of milliseconds
28213498266Sopenharmony_ci * to wait to get back under the speed limit.
28313498266Sopenharmony_ci */
28413498266Sopenharmony_citimediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
28513498266Sopenharmony_ci                                  curl_off_t startsize,
28613498266Sopenharmony_ci                                  curl_off_t limit,
28713498266Sopenharmony_ci                                  struct curltime start,
28813498266Sopenharmony_ci                                  struct curltime now)
28913498266Sopenharmony_ci{
29013498266Sopenharmony_ci  curl_off_t size = cursize - startsize;
29113498266Sopenharmony_ci  timediff_t minimum;
29213498266Sopenharmony_ci  timediff_t actual;
29313498266Sopenharmony_ci
29413498266Sopenharmony_ci  if(!limit || !size)
29513498266Sopenharmony_ci    return 0;
29613498266Sopenharmony_ci
29713498266Sopenharmony_ci  /*
29813498266Sopenharmony_ci   * 'minimum' is the number of milliseconds 'size' should take to download to
29913498266Sopenharmony_ci   * stay below 'limit'.
30013498266Sopenharmony_ci   */
30113498266Sopenharmony_ci  if(size < CURL_OFF_T_MAX/1000)
30213498266Sopenharmony_ci    minimum = (timediff_t) (CURL_OFF_T_C(1000) * size / limit);
30313498266Sopenharmony_ci  else {
30413498266Sopenharmony_ci    minimum = (timediff_t) (size / limit);
30513498266Sopenharmony_ci    if(minimum < TIMEDIFF_T_MAX/1000)
30613498266Sopenharmony_ci      minimum *= 1000;
30713498266Sopenharmony_ci    else
30813498266Sopenharmony_ci      minimum = TIMEDIFF_T_MAX;
30913498266Sopenharmony_ci  }
31013498266Sopenharmony_ci
31113498266Sopenharmony_ci  /*
31213498266Sopenharmony_ci   * 'actual' is the time in milliseconds it took to actually download the
31313498266Sopenharmony_ci   * last 'size' bytes.
31413498266Sopenharmony_ci   */
31513498266Sopenharmony_ci  actual = Curl_timediff_ceil(now, start);
31613498266Sopenharmony_ci  if(actual < minimum) {
31713498266Sopenharmony_ci    /* if it downloaded the data faster than the limit, make it wait the
31813498266Sopenharmony_ci       difference */
31913498266Sopenharmony_ci    return (minimum - actual);
32013498266Sopenharmony_ci  }
32113498266Sopenharmony_ci
32213498266Sopenharmony_ci  return 0;
32313498266Sopenharmony_ci}
32413498266Sopenharmony_ci
32513498266Sopenharmony_ci/*
32613498266Sopenharmony_ci * Set the number of downloaded bytes so far.
32713498266Sopenharmony_ci */
32813498266Sopenharmony_ciCURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
32913498266Sopenharmony_ci{
33013498266Sopenharmony_ci  data->progress.downloaded = size;
33113498266Sopenharmony_ci  return CURLE_OK;
33213498266Sopenharmony_ci}
33313498266Sopenharmony_ci
33413498266Sopenharmony_ci/*
33513498266Sopenharmony_ci * Update the timestamp and sizestamp to use for rate limit calculations.
33613498266Sopenharmony_ci */
33713498266Sopenharmony_civoid Curl_ratelimit(struct Curl_easy *data, struct curltime now)
33813498266Sopenharmony_ci{
33913498266Sopenharmony_ci  /* don't set a new stamp unless the time since last update is long enough */
34013498266Sopenharmony_ci  if(data->set.max_recv_speed) {
34113498266Sopenharmony_ci    if(Curl_timediff(now, data->progress.dl_limit_start) >=
34213498266Sopenharmony_ci       MIN_RATE_LIMIT_PERIOD) {
34313498266Sopenharmony_ci      data->progress.dl_limit_start = now;
34413498266Sopenharmony_ci      data->progress.dl_limit_size = data->progress.downloaded;
34513498266Sopenharmony_ci    }
34613498266Sopenharmony_ci  }
34713498266Sopenharmony_ci  if(data->set.max_send_speed) {
34813498266Sopenharmony_ci    if(Curl_timediff(now, data->progress.ul_limit_start) >=
34913498266Sopenharmony_ci       MIN_RATE_LIMIT_PERIOD) {
35013498266Sopenharmony_ci      data->progress.ul_limit_start = now;
35113498266Sopenharmony_ci      data->progress.ul_limit_size = data->progress.uploaded;
35213498266Sopenharmony_ci    }
35313498266Sopenharmony_ci  }
35413498266Sopenharmony_ci}
35513498266Sopenharmony_ci
35613498266Sopenharmony_ci/*
35713498266Sopenharmony_ci * Set the number of uploaded bytes so far.
35813498266Sopenharmony_ci */
35913498266Sopenharmony_civoid Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size)
36013498266Sopenharmony_ci{
36113498266Sopenharmony_ci  data->progress.uploaded = size;
36213498266Sopenharmony_ci}
36313498266Sopenharmony_ci
36413498266Sopenharmony_civoid Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size)
36513498266Sopenharmony_ci{
36613498266Sopenharmony_ci  if(size >= 0) {
36713498266Sopenharmony_ci    data->progress.size_dl = size;
36813498266Sopenharmony_ci    data->progress.flags |= PGRS_DL_SIZE_KNOWN;
36913498266Sopenharmony_ci  }
37013498266Sopenharmony_ci  else {
37113498266Sopenharmony_ci    data->progress.size_dl = 0;
37213498266Sopenharmony_ci    data->progress.flags &= ~PGRS_DL_SIZE_KNOWN;
37313498266Sopenharmony_ci  }
37413498266Sopenharmony_ci}
37513498266Sopenharmony_ci
37613498266Sopenharmony_civoid Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size)
37713498266Sopenharmony_ci{
37813498266Sopenharmony_ci  if(size >= 0) {
37913498266Sopenharmony_ci    data->progress.size_ul = size;
38013498266Sopenharmony_ci    data->progress.flags |= PGRS_UL_SIZE_KNOWN;
38113498266Sopenharmony_ci  }
38213498266Sopenharmony_ci  else {
38313498266Sopenharmony_ci    data->progress.size_ul = 0;
38413498266Sopenharmony_ci    data->progress.flags &= ~PGRS_UL_SIZE_KNOWN;
38513498266Sopenharmony_ci  }
38613498266Sopenharmony_ci}
38713498266Sopenharmony_ci
38813498266Sopenharmony_ci/* returns the average speed in bytes / second */
38913498266Sopenharmony_cistatic curl_off_t trspeed(curl_off_t size, /* number of bytes */
39013498266Sopenharmony_ci                          curl_off_t us)   /* microseconds */
39113498266Sopenharmony_ci{
39213498266Sopenharmony_ci  if(us < 1)
39313498266Sopenharmony_ci    return size * 1000000;
39413498266Sopenharmony_ci  else if(size < CURL_OFF_T_MAX/1000000)
39513498266Sopenharmony_ci    return (size * 1000000) / us;
39613498266Sopenharmony_ci  else if(us >= 1000000)
39713498266Sopenharmony_ci    return size / (us / 1000000);
39813498266Sopenharmony_ci  else
39913498266Sopenharmony_ci    return CURL_OFF_T_MAX;
40013498266Sopenharmony_ci}
40113498266Sopenharmony_ci
40213498266Sopenharmony_ci/* returns TRUE if it's time to show the progress meter */
40313498266Sopenharmony_cistatic bool progress_calc(struct Curl_easy *data, struct curltime now)
40413498266Sopenharmony_ci{
40513498266Sopenharmony_ci  bool timetoshow = FALSE;
40613498266Sopenharmony_ci  struct Progress * const p = &data->progress;
40713498266Sopenharmony_ci
40813498266Sopenharmony_ci  /* The time spent so far (from the start) in microseconds */
40913498266Sopenharmony_ci  p->timespent = Curl_timediff_us(now, p->start);
41013498266Sopenharmony_ci  p->dlspeed = trspeed(p->downloaded, p->timespent);
41113498266Sopenharmony_ci  p->ulspeed = trspeed(p->uploaded, p->timespent);
41213498266Sopenharmony_ci
41313498266Sopenharmony_ci  /* Calculations done at most once a second, unless end is reached */
41413498266Sopenharmony_ci  if(p->lastshow != now.tv_sec) {
41513498266Sopenharmony_ci    int countindex; /* amount of seconds stored in the speeder array */
41613498266Sopenharmony_ci    int nowindex = p->speeder_c% CURR_TIME;
41713498266Sopenharmony_ci    p->lastshow = now.tv_sec;
41813498266Sopenharmony_ci    timetoshow = TRUE;
41913498266Sopenharmony_ci
42013498266Sopenharmony_ci    /* Let's do the "current speed" thing, with the dl + ul speeds
42113498266Sopenharmony_ci       combined. Store the speed at entry 'nowindex'. */
42213498266Sopenharmony_ci    p->speeder[ nowindex ] = p->downloaded + p->uploaded;
42313498266Sopenharmony_ci
42413498266Sopenharmony_ci    /* remember the exact time for this moment */
42513498266Sopenharmony_ci    p->speeder_time [ nowindex ] = now;
42613498266Sopenharmony_ci
42713498266Sopenharmony_ci    /* advance our speeder_c counter, which is increased every time we get
42813498266Sopenharmony_ci       here and we expect it to never wrap as 2^32 is a lot of seconds! */
42913498266Sopenharmony_ci    p->speeder_c++;
43013498266Sopenharmony_ci
43113498266Sopenharmony_ci    /* figure out how many index entries of data we have stored in our speeder
43213498266Sopenharmony_ci       array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of
43313498266Sopenharmony_ci       transfer. Imagine, after one second we have filled in two entries,
43413498266Sopenharmony_ci       after two seconds we've filled in three entries etc. */
43513498266Sopenharmony_ci    countindex = ((p->speeder_c >= CURR_TIME)? CURR_TIME:p->speeder_c) - 1;
43613498266Sopenharmony_ci
43713498266Sopenharmony_ci    /* first of all, we don't do this if there's no counted seconds yet */
43813498266Sopenharmony_ci    if(countindex) {
43913498266Sopenharmony_ci      int checkindex;
44013498266Sopenharmony_ci      timediff_t span_ms;
44113498266Sopenharmony_ci      curl_off_t amount;
44213498266Sopenharmony_ci
44313498266Sopenharmony_ci      /* Get the index position to compare with the 'nowindex' position.
44413498266Sopenharmony_ci         Get the oldest entry possible. While we have less than CURR_TIME
44513498266Sopenharmony_ci         entries, the first entry will remain the oldest. */
44613498266Sopenharmony_ci      checkindex = (p->speeder_c >= CURR_TIME)? p->speeder_c%CURR_TIME:0;
44713498266Sopenharmony_ci
44813498266Sopenharmony_ci      /* Figure out the exact time for the time span */
44913498266Sopenharmony_ci      span_ms = Curl_timediff(now, p->speeder_time[checkindex]);
45013498266Sopenharmony_ci      if(0 == span_ms)
45113498266Sopenharmony_ci        span_ms = 1; /* at least one millisecond MUST have passed */
45213498266Sopenharmony_ci
45313498266Sopenharmony_ci      /* Calculate the average speed the last 'span_ms' milliseconds */
45413498266Sopenharmony_ci      amount = p->speeder[nowindex]- p->speeder[checkindex];
45513498266Sopenharmony_ci
45613498266Sopenharmony_ci      if(amount > CURL_OFF_T_C(4294967) /* 0xffffffff/1000 */)
45713498266Sopenharmony_ci        /* the 'amount' value is bigger than would fit in 32 bits if
45813498266Sopenharmony_ci           multiplied with 1000, so we use the double math for this */
45913498266Sopenharmony_ci        p->current_speed = (curl_off_t)
46013498266Sopenharmony_ci          ((double)amount/((double)span_ms/1000.0));
46113498266Sopenharmony_ci      else
46213498266Sopenharmony_ci        /* the 'amount' value is small enough to fit within 32 bits even
46313498266Sopenharmony_ci           when multiplied with 1000 */
46413498266Sopenharmony_ci        p->current_speed = amount*CURL_OFF_T_C(1000)/span_ms;
46513498266Sopenharmony_ci    }
46613498266Sopenharmony_ci    else
46713498266Sopenharmony_ci      /* the first second we use the average */
46813498266Sopenharmony_ci      p->current_speed = p->ulspeed + p->dlspeed;
46913498266Sopenharmony_ci
47013498266Sopenharmony_ci  } /* Calculations end */
47113498266Sopenharmony_ci  return timetoshow;
47213498266Sopenharmony_ci}
47313498266Sopenharmony_ci
47413498266Sopenharmony_ci#ifndef CURL_DISABLE_PROGRESS_METER
47513498266Sopenharmony_cistatic void progress_meter(struct Curl_easy *data)
47613498266Sopenharmony_ci{
47713498266Sopenharmony_ci  char max5[6][10];
47813498266Sopenharmony_ci  curl_off_t dlpercen = 0;
47913498266Sopenharmony_ci  curl_off_t ulpercen = 0;
48013498266Sopenharmony_ci  curl_off_t total_percen = 0;
48113498266Sopenharmony_ci  curl_off_t total_transfer;
48213498266Sopenharmony_ci  curl_off_t total_expected_transfer;
48313498266Sopenharmony_ci  char time_left[10];
48413498266Sopenharmony_ci  char time_total[10];
48513498266Sopenharmony_ci  char time_spent[10];
48613498266Sopenharmony_ci  curl_off_t ulestimate = 0;
48713498266Sopenharmony_ci  curl_off_t dlestimate = 0;
48813498266Sopenharmony_ci  curl_off_t total_estimate;
48913498266Sopenharmony_ci  curl_off_t timespent =
49013498266Sopenharmony_ci    (curl_off_t)data->progress.timespent/1000000; /* seconds */
49113498266Sopenharmony_ci
49213498266Sopenharmony_ci  if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
49313498266Sopenharmony_ci    if(data->state.resume_from) {
49413498266Sopenharmony_ci      fprintf(data->set.err,
49513498266Sopenharmony_ci              "** Resuming transfer from byte position %"
49613498266Sopenharmony_ci              CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from);
49713498266Sopenharmony_ci    }
49813498266Sopenharmony_ci    fprintf(data->set.err,
49913498266Sopenharmony_ci            "  %% Total    %% Received %% Xferd  Average Speed   "
50013498266Sopenharmony_ci            "Time    Time     Time  Current\n"
50113498266Sopenharmony_ci            "                                 Dload  Upload   "
50213498266Sopenharmony_ci            "Total   Spent    Left  Speed\n");
50313498266Sopenharmony_ci    data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */
50413498266Sopenharmony_ci  }
50513498266Sopenharmony_ci
50613498266Sopenharmony_ci  /* Figure out the estimated time of arrival for the upload */
50713498266Sopenharmony_ci  if((data->progress.flags & PGRS_UL_SIZE_KNOWN) &&
50813498266Sopenharmony_ci     (data->progress.ulspeed > CURL_OFF_T_C(0))) {
50913498266Sopenharmony_ci    ulestimate = data->progress.size_ul / data->progress.ulspeed;
51013498266Sopenharmony_ci
51113498266Sopenharmony_ci    if(data->progress.size_ul > CURL_OFF_T_C(10000))
51213498266Sopenharmony_ci      ulpercen = data->progress.uploaded /
51313498266Sopenharmony_ci        (data->progress.size_ul/CURL_OFF_T_C(100));
51413498266Sopenharmony_ci    else if(data->progress.size_ul > CURL_OFF_T_C(0))
51513498266Sopenharmony_ci      ulpercen = (data->progress.uploaded*100) /
51613498266Sopenharmony_ci        data->progress.size_ul;
51713498266Sopenharmony_ci  }
51813498266Sopenharmony_ci
51913498266Sopenharmony_ci  /* ... and the download */
52013498266Sopenharmony_ci  if((data->progress.flags & PGRS_DL_SIZE_KNOWN) &&
52113498266Sopenharmony_ci     (data->progress.dlspeed > CURL_OFF_T_C(0))) {
52213498266Sopenharmony_ci    dlestimate = data->progress.size_dl / data->progress.dlspeed;
52313498266Sopenharmony_ci
52413498266Sopenharmony_ci    if(data->progress.size_dl > CURL_OFF_T_C(10000))
52513498266Sopenharmony_ci      dlpercen = data->progress.downloaded /
52613498266Sopenharmony_ci        (data->progress.size_dl/CURL_OFF_T_C(100));
52713498266Sopenharmony_ci    else if(data->progress.size_dl > CURL_OFF_T_C(0))
52813498266Sopenharmony_ci      dlpercen = (data->progress.downloaded*100) /
52913498266Sopenharmony_ci        data->progress.size_dl;
53013498266Sopenharmony_ci  }
53113498266Sopenharmony_ci
53213498266Sopenharmony_ci  /* Now figure out which of them is slower and use that one for the
53313498266Sopenharmony_ci     total estimate! */
53413498266Sopenharmony_ci  total_estimate = ulestimate>dlestimate?ulestimate:dlestimate;
53513498266Sopenharmony_ci
53613498266Sopenharmony_ci  /* create the three time strings */
53713498266Sopenharmony_ci  time2str(time_left, total_estimate > 0?(total_estimate - timespent):0);
53813498266Sopenharmony_ci  time2str(time_total, total_estimate);
53913498266Sopenharmony_ci  time2str(time_spent, timespent);
54013498266Sopenharmony_ci
54113498266Sopenharmony_ci  /* Get the total amount of data expected to get transferred */
54213498266Sopenharmony_ci  total_expected_transfer =
54313498266Sopenharmony_ci    ((data->progress.flags & PGRS_UL_SIZE_KNOWN)?
54413498266Sopenharmony_ci     data->progress.size_ul:data->progress.uploaded)+
54513498266Sopenharmony_ci    ((data->progress.flags & PGRS_DL_SIZE_KNOWN)?
54613498266Sopenharmony_ci     data->progress.size_dl:data->progress.downloaded);
54713498266Sopenharmony_ci
54813498266Sopenharmony_ci  /* We have transferred this much so far */
54913498266Sopenharmony_ci  total_transfer = data->progress.downloaded + data->progress.uploaded;
55013498266Sopenharmony_ci
55113498266Sopenharmony_ci  /* Get the percentage of data transferred so far */
55213498266Sopenharmony_ci  if(total_expected_transfer > CURL_OFF_T_C(10000))
55313498266Sopenharmony_ci    total_percen = total_transfer /
55413498266Sopenharmony_ci      (total_expected_transfer/CURL_OFF_T_C(100));
55513498266Sopenharmony_ci  else if(total_expected_transfer > CURL_OFF_T_C(0))
55613498266Sopenharmony_ci    total_percen = (total_transfer*100) / total_expected_transfer;
55713498266Sopenharmony_ci
55813498266Sopenharmony_ci  fprintf(data->set.err,
55913498266Sopenharmony_ci          "\r"
56013498266Sopenharmony_ci          "%3" CURL_FORMAT_CURL_OFF_T " %s  "
56113498266Sopenharmony_ci          "%3" CURL_FORMAT_CURL_OFF_T " %s  "
56213498266Sopenharmony_ci          "%3" CURL_FORMAT_CURL_OFF_T " %s  %s  %s %s %s %s %s",
56313498266Sopenharmony_ci          total_percen,  /* 3 letters */                /* total % */
56413498266Sopenharmony_ci          max5data(total_expected_transfer, max5[2]),   /* total size */
56513498266Sopenharmony_ci          dlpercen,      /* 3 letters */                /* rcvd % */
56613498266Sopenharmony_ci          max5data(data->progress.downloaded, max5[0]), /* rcvd size */
56713498266Sopenharmony_ci          ulpercen,      /* 3 letters */                /* xfer % */
56813498266Sopenharmony_ci          max5data(data->progress.uploaded, max5[1]),   /* xfer size */
56913498266Sopenharmony_ci          max5data(data->progress.dlspeed, max5[3]),    /* avrg dl speed */
57013498266Sopenharmony_ci          max5data(data->progress.ulspeed, max5[4]),    /* avrg ul speed */
57113498266Sopenharmony_ci          time_total,    /* 8 letters */                /* total time */
57213498266Sopenharmony_ci          time_spent,    /* 8 letters */                /* time spent */
57313498266Sopenharmony_ci          time_left,     /* 8 letters */                /* time left */
57413498266Sopenharmony_ci          max5data(data->progress.current_speed, max5[5])
57513498266Sopenharmony_ci    );
57613498266Sopenharmony_ci
57713498266Sopenharmony_ci  /* we flush the output stream to make it appear as soon as possible */
57813498266Sopenharmony_ci  fflush(data->set.err);
57913498266Sopenharmony_ci}
58013498266Sopenharmony_ci#else
58113498266Sopenharmony_ci /* progress bar disabled */
58213498266Sopenharmony_ci#define progress_meter(x) Curl_nop_stmt
58313498266Sopenharmony_ci#endif
58413498266Sopenharmony_ci
58513498266Sopenharmony_ci
58613498266Sopenharmony_ci/*
58713498266Sopenharmony_ci * Curl_pgrsUpdate() returns 0 for success or the value returned by the
58813498266Sopenharmony_ci * progress callback!
58913498266Sopenharmony_ci */
59013498266Sopenharmony_ciint Curl_pgrsUpdate(struct Curl_easy *data)
59113498266Sopenharmony_ci{
59213498266Sopenharmony_ci  struct curltime now = Curl_now(); /* what time is it */
59313498266Sopenharmony_ci  bool showprogress = progress_calc(data, now);
59413498266Sopenharmony_ci  if(!(data->progress.flags & PGRS_HIDE)) {
59513498266Sopenharmony_ci    if(data->set.fxferinfo) {
59613498266Sopenharmony_ci      int result;
59713498266Sopenharmony_ci      /* There's a callback set, call that */
59813498266Sopenharmony_ci      Curl_set_in_callback(data, true);
59913498266Sopenharmony_ci      result = data->set.fxferinfo(data->set.progress_client,
60013498266Sopenharmony_ci                                   data->progress.size_dl,
60113498266Sopenharmony_ci                                   data->progress.downloaded,
60213498266Sopenharmony_ci                                   data->progress.size_ul,
60313498266Sopenharmony_ci                                   data->progress.uploaded);
60413498266Sopenharmony_ci      Curl_set_in_callback(data, false);
60513498266Sopenharmony_ci      if(result != CURL_PROGRESSFUNC_CONTINUE) {
60613498266Sopenharmony_ci        if(result)
60713498266Sopenharmony_ci          failf(data, "Callback aborted");
60813498266Sopenharmony_ci        return result;
60913498266Sopenharmony_ci      }
61013498266Sopenharmony_ci    }
61113498266Sopenharmony_ci    else if(data->set.fprogress) {
61213498266Sopenharmony_ci      int result;
61313498266Sopenharmony_ci      /* The older deprecated callback is set, call that */
61413498266Sopenharmony_ci      Curl_set_in_callback(data, true);
61513498266Sopenharmony_ci      result = data->set.fprogress(data->set.progress_client,
61613498266Sopenharmony_ci                                   (double)data->progress.size_dl,
61713498266Sopenharmony_ci                                   (double)data->progress.downloaded,
61813498266Sopenharmony_ci                                   (double)data->progress.size_ul,
61913498266Sopenharmony_ci                                   (double)data->progress.uploaded);
62013498266Sopenharmony_ci      Curl_set_in_callback(data, false);
62113498266Sopenharmony_ci      if(result != CURL_PROGRESSFUNC_CONTINUE) {
62213498266Sopenharmony_ci        if(result)
62313498266Sopenharmony_ci          failf(data, "Callback aborted");
62413498266Sopenharmony_ci        return result;
62513498266Sopenharmony_ci      }
62613498266Sopenharmony_ci    }
62713498266Sopenharmony_ci
62813498266Sopenharmony_ci    if(showprogress)
62913498266Sopenharmony_ci      progress_meter(data);
63013498266Sopenharmony_ci  }
63113498266Sopenharmony_ci
63213498266Sopenharmony_ci  return 0;
63313498266Sopenharmony_ci}
634