Coverage Report

Created: 2025-06-20 06:58

/src/PROJ/curl/lib/progress.c
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
25
#include "curl_setup.h"
26
27
#include "urldata.h"
28
#include "sendf.h"
29
#include "multiif.h"
30
#include "progress.h"
31
#include "curlx/timeval.h"
32
#include "curl_printf.h"
33
34
/* check rate limits within this many recent milliseconds, at minimum. */
35
0
#define MIN_RATE_LIMIT_PERIOD 3000
36
37
#ifndef CURL_DISABLE_PROGRESS_METER
38
/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero
39
   byte) */
40
static void time2str(char *r, curl_off_t seconds)
41
0
{
42
0
  curl_off_t h;
43
0
  if(seconds <= 0) {
44
0
    strcpy(r, "--:--:--");
45
0
    return;
46
0
  }
47
0
  h = seconds / 3600;
48
0
  if(h <= 99) {
49
0
    curl_off_t m = (seconds - (h * 3600)) / 60;
50
0
    curl_off_t s = (seconds - (h * 3600)) - (m * 60);
51
0
    msnprintf(r, 9, "%2" FMT_OFF_T ":%02" FMT_OFF_T ":%02" FMT_OFF_T, h, m, s);
52
0
  }
53
0
  else {
54
    /* this equals to more than 99 hours, switch to a more suitable output
55
       format to fit within the limits. */
56
0
    curl_off_t d = seconds / 86400;
57
0
    h = (seconds - (d * 86400)) / 3600;
58
0
    if(d <= 999)
59
0
      msnprintf(r, 9, "%3" FMT_OFF_T "d %02" FMT_OFF_T "h", d, h);
60
0
    else
61
0
      msnprintf(r, 9, "%7" FMT_OFF_T "d", d);
62
0
  }
63
0
}
64
65
/* The point of this function would be to return a string of the input data,
66
   but never longer than 5 columns (+ one zero byte).
67
   Add suffix k, M, G when suitable... */
68
static char *max5data(curl_off_t bytes, char *max5)
69
0
{
70
0
#define ONE_KILOBYTE (curl_off_t)1024
71
0
#define ONE_MEGABYTE (1024 * ONE_KILOBYTE)
72
0
#define ONE_GIGABYTE (1024 * ONE_MEGABYTE)
73
0
#define ONE_TERABYTE (1024 * ONE_GIGABYTE)
74
0
#define ONE_PETABYTE (1024 * ONE_TERABYTE)
75
76
0
  if(bytes < 100000)
77
0
    msnprintf(max5, 6, "%5" FMT_OFF_T, bytes);
78
79
0
  else if(bytes < 10000 * ONE_KILOBYTE)
80
0
    msnprintf(max5, 6, "%4" FMT_OFF_T "k", bytes/ONE_KILOBYTE);
81
82
0
  else if(bytes < 100 * ONE_MEGABYTE)
83
    /* 'XX.XM' is good as long as we are less than 100 megs */
84
0
    msnprintf(max5, 6, "%2" FMT_OFF_T ".%0"
85
0
              FMT_OFF_T "M", bytes/ONE_MEGABYTE,
86
0
              (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/10) );
87
88
0
  else if(bytes < 10000 * ONE_MEGABYTE)
89
    /* 'XXXXM' is good until we are at 10000MB or above */
90
0
    msnprintf(max5, 6, "%4" FMT_OFF_T "M", bytes/ONE_MEGABYTE);
91
92
0
  else if(bytes < 100 * ONE_GIGABYTE)
93
    /* 10000 MB - 100 GB, we show it as XX.XG */
94
0
    msnprintf(max5, 6, "%2" FMT_OFF_T ".%0"
95
0
              FMT_OFF_T "G", bytes/ONE_GIGABYTE,
96
0
              (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/10) );
97
98
0
  else if(bytes < 10000 * ONE_GIGABYTE)
99
    /* up to 10000GB, display without decimal: XXXXG */
100
0
    msnprintf(max5, 6, "%4" FMT_OFF_T "G", bytes/ONE_GIGABYTE);
101
102
0
  else if(bytes < 10000 * ONE_TERABYTE)
103
    /* up to 10000TB, display without decimal: XXXXT */
104
0
    msnprintf(max5, 6, "%4" FMT_OFF_T "T", bytes/ONE_TERABYTE);
105
106
0
  else
107
    /* up to 10000PB, display without decimal: XXXXP */
108
0
    msnprintf(max5, 6, "%4" FMT_OFF_T "P", bytes/ONE_PETABYTE);
109
110
  /* 16384 petabytes (16 exabytes) is the maximum a 64-bit unsigned number can
111
     hold, but our data type is signed so 8192PB will be the maximum. */
112
113
0
  return max5;
114
0
}
115
#endif
116
117
/*
118
119
   New proposed interface, 9th of February 2000:
120
121
   pgrsStartNow() - sets start time
122
   pgrsSetDownloadSize(x) - known expected download size
123
   pgrsSetUploadSize(x) - known expected upload size
124
   pgrsSetDownloadCounter() - amount of data currently downloaded
125
   pgrsSetUploadCounter() - amount of data currently uploaded
126
   pgrsUpdate() - show progress
127
   pgrsDone() - transfer complete
128
129
*/
130
131
int Curl_pgrsDone(struct Curl_easy *data)
132
0
{
133
0
  int rc;
134
0
  data->progress.lastshow = 0;
135
0
  rc = Curl_pgrsUpdate(data); /* the final (forced) update */
136
0
  if(rc)
137
0
    return rc;
138
139
0
  if(!data->progress.hide && !data->progress.callback)
140
    /* only output if we do not use a progress callback and we are not
141
     * hidden */
142
0
    fprintf(data->set.err, "\n");
143
144
0
  data->progress.speeder_c = 0; /* reset the progress meter display */
145
0
  return 0;
146
0
}
147
148
/* reset the known transfer sizes */
149
void Curl_pgrsResetTransferSizes(struct Curl_easy *data)
150
0
{
151
0
  Curl_pgrsSetDownloadSize(data, -1);
152
0
  Curl_pgrsSetUploadSize(data, -1);
153
0
}
154
155
/*
156
 *
157
 * Curl_pgrsTimeWas(). Store the timestamp time at the given label.
158
 */
159
void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
160
                      struct curltime timestamp)
161
0
{
162
0
  timediff_t *delta = NULL;
163
164
0
  switch(timer) {
165
0
  default:
166
0
  case TIMER_NONE:
167
    /* mistake filter */
168
0
    break;
169
0
  case TIMER_STARTOP:
170
    /* This is set at the start of a transfer */
171
0
    data->progress.t_startop = timestamp;
172
0
    data->progress.t_startqueue = timestamp;
173
0
    data->progress.t_postqueue = 0;
174
0
    break;
175
0
  case TIMER_STARTSINGLE:
176
    /* This is set at the start of each single transfer */
177
0
    data->progress.t_startsingle = timestamp;
178
0
    data->progress.is_t_startransfer_set = FALSE;
179
0
    break;
180
0
  case TIMER_POSTQUEUE:
181
    /* Queue time is accumulative from all involved redirects */
182
0
    data->progress.t_postqueue +=
183
0
      curlx_timediff_us(timestamp, data->progress.t_startqueue);
184
0
    break;
185
0
  case TIMER_STARTACCEPT:
186
0
    data->progress.t_acceptdata = timestamp;
187
0
    break;
188
0
  case TIMER_NAMELOOKUP:
189
0
    delta = &data->progress.t_nslookup;
190
0
    break;
191
0
  case TIMER_CONNECT:
192
0
    delta = &data->progress.t_connect;
193
0
    break;
194
0
  case TIMER_APPCONNECT:
195
0
    delta = &data->progress.t_appconnect;
196
0
    break;
197
0
  case TIMER_PRETRANSFER:
198
0
    delta = &data->progress.t_pretransfer;
199
0
    break;
200
0
  case TIMER_STARTTRANSFER:
201
0
    delta = &data->progress.t_starttransfer;
202
    /* prevent updating t_starttransfer unless:
203
     *   1) this is the first time we are setting t_starttransfer
204
     *   2) a redirect has occurred since the last time t_starttransfer was set
205
     * This prevents repeated invocations of the function from incorrectly
206
     * changing the t_starttransfer time.
207
     */
208
0
    if(data->progress.is_t_startransfer_set) {
209
0
      return;
210
0
    }
211
0
    else {
212
0
      data->progress.is_t_startransfer_set = TRUE;
213
0
      break;
214
0
    }
215
0
  case TIMER_POSTRANSFER:
216
0
    delta = &data->progress.t_posttransfer;
217
0
    break;
218
0
  case TIMER_REDIRECT:
219
0
    data->progress.t_redirect = curlx_timediff_us(timestamp,
220
0
                                                 data->progress.start);
221
0
    data->progress.t_startqueue = timestamp;
222
0
    break;
223
0
  }
224
0
  if(delta) {
225
0
    timediff_t us = curlx_timediff_us(timestamp, data->progress.t_startsingle);
226
0
    if(us < 1)
227
0
      us = 1; /* make sure at least one microsecond passed */
228
0
    *delta += us;
229
0
  }
230
0
}
231
232
/*
233
 *
234
 * Curl_pgrsTime(). Store the current time at the given label. This fetches a
235
 * fresh "now" and returns it.
236
 *
237
 * @unittest: 1399
238
 */
239
struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
240
0
{
241
0
  struct curltime now = curlx_now();
242
243
0
  Curl_pgrsTimeWas(data, timer, now);
244
0
  return now;
245
0
}
246
247
void Curl_pgrsStartNow(struct Curl_easy *data)
248
0
{
249
0
  struct Progress *p = &data->progress;
250
0
  p->speeder_c = 0; /* reset the progress meter display */
251
0
  p->start = curlx_now();
252
0
  p->is_t_startransfer_set = FALSE;
253
0
  p->ul.limit.start = p->start;
254
0
  p->dl.limit.start = p->start;
255
0
  p->ul.limit.start_size = 0;
256
0
  p->dl.limit.start_size = 0;
257
0
  p->dl.cur_size = 0;
258
0
  p->ul.cur_size = 0;
259
  /* the sizes are unknown at start */
260
0
  p->dl_size_known = FALSE;
261
0
  p->ul_size_known = FALSE;
262
0
  Curl_ratelimit(data, p->start);
263
0
}
264
265
/*
266
 * This is used to handle speed limits, calculating how many milliseconds to
267
 * wait until we are back under the speed limit, if needed.
268
 *
269
 * The way it works is by having a "starting point" (time & amount of data
270
 * transferred by then) used in the speed computation, to be used instead of
271
 * the start of the transfer. This starting point is regularly moved as
272
 * transfer goes on, to keep getting accurate values (instead of average over
273
 * the entire transfer).
274
 *
275
 * This function takes the current amount of data transferred, the amount at
276
 * the starting point, the limit (in bytes/s), the time of the starting point
277
 * and the current time.
278
 *
279
 * Returns 0 if no waiting is needed or when no waiting is needed but the
280
 * starting point should be reset (to current); or the number of milliseconds
281
 * to wait to get back under the speed limit.
282
 */
283
timediff_t Curl_pgrsLimitWaitTime(struct pgrs_dir *d,
284
                                  curl_off_t speed_limit,
285
                                  struct curltime now)
286
0
{
287
0
  curl_off_t size = d->cur_size - d->limit.start_size;
288
0
  timediff_t minimum;
289
0
  timediff_t actual;
290
291
0
  if(!speed_limit || !size)
292
0
    return 0;
293
294
  /*
295
   * 'minimum' is the number of milliseconds 'size' should take to download to
296
   * stay below 'limit'.
297
   */
298
0
  if(size < CURL_OFF_T_MAX/1000)
299
0
    minimum = (timediff_t) (1000 * size / speed_limit);
300
0
  else {
301
0
    minimum = (timediff_t) (size / speed_limit);
302
0
    if(minimum < TIMEDIFF_T_MAX/1000)
303
0
      minimum *= 1000;
304
0
    else
305
0
      minimum = TIMEDIFF_T_MAX;
306
0
  }
307
308
  /*
309
   * 'actual' is the time in milliseconds it took to actually download the
310
   * last 'size' bytes.
311
   */
312
0
  actual = curlx_timediff_ceil(now, d->limit.start);
313
0
  if(actual < minimum) {
314
    /* if it downloaded the data faster than the limit, make it wait the
315
       difference */
316
0
    return minimum - actual;
317
0
  }
318
319
0
  return 0;
320
0
}
321
322
/*
323
 * Set the number of downloaded bytes so far.
324
 */
325
CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
326
0
{
327
0
  data->progress.dl.cur_size = size;
328
0
  return CURLE_OK;
329
0
}
330
331
/*
332
 * Update the timestamp and sizestamp to use for rate limit calculations.
333
 */
334
void Curl_ratelimit(struct Curl_easy *data, struct curltime now)
335
0
{
336
  /* do not set a new stamp unless the time since last update is long enough */
337
0
  if(data->set.max_recv_speed) {
338
0
    if(curlx_timediff(now, data->progress.dl.limit.start) >=
339
0
       MIN_RATE_LIMIT_PERIOD) {
340
0
      data->progress.dl.limit.start = now;
341
0
      data->progress.dl.limit.start_size = data->progress.dl.cur_size;
342
0
    }
343
0
  }
344
0
  if(data->set.max_send_speed) {
345
0
    if(curlx_timediff(now, data->progress.ul.limit.start) >=
346
0
       MIN_RATE_LIMIT_PERIOD) {
347
0
      data->progress.ul.limit.start = now;
348
0
      data->progress.ul.limit.start_size = data->progress.ul.cur_size;
349
0
    }
350
0
  }
351
0
}
352
353
/*
354
 * Set the number of uploaded bytes so far.
355
 */
356
void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size)
357
0
{
358
0
  data->progress.ul.cur_size = size;
359
0
}
360
361
void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size)
362
0
{
363
0
  if(size >= 0) {
364
0
    data->progress.dl.total_size = size;
365
0
    data->progress.dl_size_known = TRUE;
366
0
  }
367
0
  else {
368
0
    data->progress.dl.total_size = 0;
369
0
    data->progress.dl_size_known = FALSE;
370
0
  }
371
0
}
372
373
void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size)
374
0
{
375
0
  if(size >= 0) {
376
0
    data->progress.ul.total_size = size;
377
0
    data->progress.ul_size_known = TRUE;
378
0
  }
379
0
  else {
380
0
    data->progress.ul.total_size = 0;
381
0
    data->progress.ul_size_known = FALSE;
382
0
  }
383
0
}
384
385
void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent)
386
0
{
387
0
    data->progress.earlydata_sent = sent;
388
0
}
389
390
/* returns the average speed in bytes / second */
391
static curl_off_t trspeed(curl_off_t size, /* number of bytes */
392
                          curl_off_t us)   /* microseconds */
393
0
{
394
0
  if(us < 1)
395
0
    return size * 1000000;
396
0
  else if(size < CURL_OFF_T_MAX/1000000)
397
0
    return (size * 1000000) / us;
398
0
  else if(us >= 1000000)
399
0
    return size / (us / 1000000);
400
0
  else
401
0
    return CURL_OFF_T_MAX;
402
0
}
403
404
/* returns TRUE if it is time to show the progress meter */
405
static bool progress_calc(struct Curl_easy *data, struct curltime now)
406
0
{
407
0
  bool timetoshow = FALSE;
408
0
  struct Progress * const p = &data->progress;
409
410
  /* The time spent so far (from the start) in microseconds */
411
0
  p->timespent = curlx_timediff_us(now, p->start);
412
0
  p->dl.speed = trspeed(p->dl.cur_size, p->timespent);
413
0
  p->ul.speed = trspeed(p->ul.cur_size, p->timespent);
414
415
  /* Calculations done at most once a second, unless end is reached */
416
0
  if(p->lastshow != now.tv_sec) {
417
0
    int countindex; /* amount of seconds stored in the speeder array */
418
0
    int nowindex = p->speeder_c% CURR_TIME;
419
0
    p->lastshow = now.tv_sec;
420
0
    timetoshow = TRUE;
421
422
    /* Let's do the "current speed" thing, with the dl + ul speeds
423
       combined. Store the speed at entry 'nowindex'. */
424
0
    p->speeder[ nowindex ] = p->dl.cur_size + p->ul.cur_size;
425
426
    /* remember the exact time for this moment */
427
0
    p->speeder_time [ nowindex ] = now;
428
429
    /* advance our speeder_c counter, which is increased every time we get
430
       here and we expect it to never wrap as 2^32 is a lot of seconds! */
431
0
    p->speeder_c++;
432
433
    /* figure out how many index entries of data we have stored in our speeder
434
       array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of
435
       transfer. Imagine, after one second we have filled in two entries,
436
       after two seconds we have filled in three entries etc. */
437
0
    countindex = ((p->speeder_c >= CURR_TIME) ? CURR_TIME : p->speeder_c) - 1;
438
439
    /* first of all, we do not do this if there is no counted seconds yet */
440
0
    if(countindex) {
441
0
      int checkindex;
442
0
      timediff_t span_ms;
443
0
      curl_off_t amount;
444
445
      /* Get the index position to compare with the 'nowindex' position.
446
         Get the oldest entry possible. While we have less than CURR_TIME
447
         entries, the first entry will remain the oldest. */
448
0
      checkindex = (p->speeder_c >= CURR_TIME) ? p->speeder_c%CURR_TIME : 0;
449
450
      /* Figure out the exact time for the time span */
451
0
      span_ms = curlx_timediff(now, p->speeder_time[checkindex]);
452
0
      if(0 == span_ms)
453
0
        span_ms = 1; /* at least one millisecond MUST have passed */
454
455
      /* Calculate the average speed the last 'span_ms' milliseconds */
456
0
      amount = p->speeder[nowindex]- p->speeder[checkindex];
457
458
0
      if(amount > (0xffffffff/1000))
459
        /* the 'amount' value is bigger than would fit in 32 bits if
460
           multiplied with 1000, so we use the double math for this */
461
0
        p->current_speed = (curl_off_t)
462
0
          ((double)amount/((double)span_ms/1000.0));
463
0
      else
464
        /* the 'amount' value is small enough to fit within 32 bits even
465
           when multiplied with 1000 */
466
0
        p->current_speed = amount * 1000/span_ms;
467
0
    }
468
0
    else
469
      /* the first second we use the average */
470
0
      p->current_speed = p->ul.speed + p->dl.speed;
471
472
0
  } /* Calculations end */
473
0
  return timetoshow;
474
0
}
475
476
#ifndef CURL_DISABLE_PROGRESS_METER
477
478
struct pgrs_estimate {
479
  curl_off_t secs;
480
  curl_off_t percent;
481
};
482
483
static curl_off_t pgrs_est_percent(curl_off_t total, curl_off_t cur)
484
0
{
485
0
  if(total > 10000)
486
0
    return cur / (total / 100);
487
0
  else if(total > 0)
488
0
    return (cur*100) / total;
489
0
  return 0;
490
0
}
491
492
static void pgrs_estimates(struct pgrs_dir *d,
493
                           bool total_known,
494
                           struct pgrs_estimate *est)
495
0
{
496
0
  est->secs = 0;
497
0
  est->percent = 0;
498
0
  if(total_known && (d->speed > 0)) {
499
0
    est->secs = d->total_size / d->speed;
500
0
    est->percent = pgrs_est_percent(d->total_size, d->cur_size);
501
0
  }
502
0
}
503
504
static void progress_meter(struct Curl_easy *data)
505
0
{
506
0
  struct Progress *p = &data->progress;
507
0
  char max5[6][10];
508
0
  struct pgrs_estimate dl_estm;
509
0
  struct pgrs_estimate ul_estm;
510
0
  struct pgrs_estimate total_estm;
511
0
  curl_off_t total_cur_size;
512
0
  curl_off_t total_expected_size;
513
0
  curl_off_t dl_size;
514
0
  char time_left[10];
515
0
  char time_total[10];
516
0
  char time_spent[10];
517
0
  curl_off_t cur_secs = (curl_off_t)p->timespent/1000000; /* seconds */
518
519
0
  if(!p->headers_out) {
520
0
    if(data->state.resume_from) {
521
0
      fprintf(data->set.err,
522
0
              "** Resuming transfer from byte position %" FMT_OFF_T "\n",
523
0
              data->state.resume_from);
524
0
    }
525
0
    fprintf(data->set.err,
526
0
            "  %% Total    %% Received %% Xferd  Average Speed   "
527
0
            "Time    Time     Time  Current\n"
528
0
            "                                 Dload  Upload   "
529
0
            "Total   Spent    Left  Speed\n");
530
0
    p->headers_out = TRUE; /* headers are shown */
531
0
  }
532
533
  /* Figure out the estimated time of arrival for upload and download */
534
0
  pgrs_estimates(&p->ul, (bool)p->ul_size_known, &ul_estm);
535
0
  pgrs_estimates(&p->dl, (bool)p->dl_size_known, &dl_estm);
536
537
  /* Since both happen at the same time, total expected duration is max. */
538
0
  total_estm.secs = CURLMAX(ul_estm.secs, dl_estm.secs);
539
  /* create the three time strings */
540
0
  time2str(time_left, total_estm.secs > 0 ? (total_estm.secs - cur_secs) : 0);
541
0
  time2str(time_total, total_estm.secs);
542
0
  time2str(time_spent, cur_secs);
543
544
  /* Get the total amount of data expected to get transferred */
545
0
  total_expected_size =
546
0
    p->ul_size_known ? p->ul.total_size : p->ul.cur_size;
547
548
0
  dl_size =
549
0
    p->dl_size_known ? p->dl.total_size : p->dl.cur_size;
550
551
  /* integer overflow check */
552
0
  if((CURL_OFF_T_MAX - total_expected_size) < dl_size)
553
0
    total_expected_size = CURL_OFF_T_MAX; /* capped */
554
0
  else
555
0
    total_expected_size += dl_size;
556
557
  /* We have transferred this much so far */
558
0
  total_cur_size = p->dl.cur_size + p->ul.cur_size;
559
560
  /* Get the percentage of data transferred so far */
561
0
  total_estm.percent = pgrs_est_percent(total_expected_size, total_cur_size);
562
563
0
  fprintf(data->set.err,
564
0
          "\r"
565
0
          "%3" FMT_OFF_T " %s  "
566
0
          "%3" FMT_OFF_T " %s  "
567
0
          "%3" FMT_OFF_T " %s  %s  %s %s %s %s %s",
568
0
          total_estm.percent, /* 3 letters */           /* total % */
569
0
          max5data(total_expected_size, max5[2]),       /* total size */
570
0
          dl_estm.percent, /* 3 letters */              /* rcvd % */
571
0
          max5data(p->dl.cur_size, max5[0]),            /* rcvd size */
572
0
          ul_estm.percent, /* 3 letters */              /* xfer % */
573
0
          max5data(p->ul.cur_size, max5[1]),            /* xfer size */
574
0
          max5data(p->dl.speed, max5[3]),               /* avrg dl speed */
575
0
          max5data(p->ul.speed, max5[4]),               /* avrg ul speed */
576
0
          time_total,    /* 8 letters */                /* total time */
577
0
          time_spent,    /* 8 letters */                /* time spent */
578
0
          time_left,     /* 8 letters */                /* time left */
579
0
          max5data(p->current_speed, max5[5])
580
0
    );
581
582
  /* we flush the output stream to make it appear as soon as possible */
583
0
  fflush(data->set.err);
584
0
}
585
#else
586
 /* progress bar disabled */
587
#define progress_meter(x) Curl_nop_stmt
588
#endif
589
590
591
/*
592
 * Curl_pgrsUpdate() returns 0 for success or the value returned by the
593
 * progress callback!
594
 */
595
static int pgrsupdate(struct Curl_easy *data, bool showprogress)
596
0
{
597
0
  if(!data->progress.hide) {
598
0
    if(data->set.fxferinfo) {
599
0
      int result;
600
      /* There is a callback set, call that */
601
0
      Curl_set_in_callback(data, TRUE);
602
0
      result = data->set.fxferinfo(data->set.progress_client,
603
0
                                   data->progress.dl.total_size,
604
0
                                   data->progress.dl.cur_size,
605
0
                                   data->progress.ul.total_size,
606
0
                                   data->progress.ul.cur_size);
607
0
      Curl_set_in_callback(data, FALSE);
608
0
      if(result != CURL_PROGRESSFUNC_CONTINUE) {
609
0
        if(result)
610
0
          failf(data, "Callback aborted");
611
0
        return result;
612
0
      }
613
0
    }
614
0
    else if(data->set.fprogress) {
615
0
      int result;
616
      /* The older deprecated callback is set, call that */
617
0
      Curl_set_in_callback(data, TRUE);
618
0
      result = data->set.fprogress(data->set.progress_client,
619
0
                                   (double)data->progress.dl.total_size,
620
0
                                   (double)data->progress.dl.cur_size,
621
0
                                   (double)data->progress.ul.total_size,
622
0
                                   (double)data->progress.ul.cur_size);
623
0
      Curl_set_in_callback(data, FALSE);
624
0
      if(result != CURL_PROGRESSFUNC_CONTINUE) {
625
0
        if(result)
626
0
          failf(data, "Callback aborted");
627
0
        return result;
628
0
      }
629
0
    }
630
631
0
    if(showprogress)
632
0
      progress_meter(data);
633
0
  }
634
635
0
  return 0;
636
0
}
637
638
int Curl_pgrsUpdate(struct Curl_easy *data)
639
0
{
640
0
  struct curltime now = curlx_now(); /* what time is it */
641
0
  bool showprogress = progress_calc(data, now);
642
0
  return pgrsupdate(data, showprogress);
643
0
}
644
645
/*
646
 * Update all progress, do not do progress meter/callbacks.
647
 */
648
void Curl_pgrsUpdate_nometer(struct Curl_easy *data)
649
0
{
650
0
  struct curltime now = curlx_now(); /* what time is it */
651
0
  (void)progress_calc(data, now);
652
0
}