aboutsummaryrefslogtreecommitdiff
path: root/lib/progress.c
blob: 1bd5b470c176c21f27c646674b70b0dc0241cd72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
/*****************************************************************************
 *                                  _   _ ____  _     
 *  Project                     ___| | | |  _ \| |    
 *                             / __| | | | |_) | |    
 *                            | (__| |_| |  _ <| |___ 
 *                             \___|\___/|_| \_\_____|
 *
 *  The contents of this file are subject to the Mozilla Public License
 *  Version 1.0 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *  http://www.mozilla.org/MPL/
 *
 *  Software distributed under the License is distributed on an "AS IS"
 *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 *  License for the specific language governing rights and limitations
 *  under the License.
 *
 *  The Original Code is Curl.
 *
 *  The Initial Developer of the Original Code is Daniel Stenberg.
 *
 *  Portions created by the Initial Developer are Copyright (C) 1998.
 *  All Rights Reserved.
 *
 * ------------------------------------------------------------
 * Main author:
 * - Daniel Stenberg <Daniel.Stenberg@haxx.nu>
 *
 * 	http://curl.haxx.nu
 *
 * $Source$
 * $Revision$
 * $Date$
 * $Author$
 * $State$
 * $Locker$
 *
 * ------------------------------------------------------------
 ****************************************************************************/

#include <string.h>
#include "setup.h"

#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
#if defined(__MINGW32__)
#include <winsock.h>
#endif
#include <time.h>
#endif

/* 20000318 mgs
 * later we use _scrsize to determine the screen width, this emx library
 * function needs stdlib.h to be included */
#if defined(__EMX__)
#include <stdlib.h>
#endif

#include <curl/curl.h>
#include "urldata.h"

#include "progress.h"

void time2str(char *r, int t)
{
  int h = (t/3600);
  int m = (t-(h*3600))/60;
  int s = (t-(h*3600)-(m*60));
  sprintf(r,"%2d:%02d:%02d",h,m,s);
}

/* The point of this function would be to return a string of the input data,
   but never longer than 5 columns. Add suffix k, M, G when suitable... */
char *max5data(double bytes, char *max5)
{
  if(bytes < 100000) {
    sprintf(max5, "%5d", (int)bytes);
    return max5;
  }
  if(bytes < (9999*1024)) {
    sprintf(max5, "%4dk", (int)bytes/1024);
    return max5;
  }
  sprintf(max5, "%4dM", (int)bytes/(1024*1024));
  return max5;
}

/* 

   New proposed interface, 9th of February 2000:

   pgrsStartNow() - sets start time
   pgrsMode(type) - kind of display
   pgrsSetDownloadSize(x) - known expected download size
   pgrsSetUploadSize(x) - known expected upload size
   pgrsSetDownloadCounter() - amount of data currently downloaded
   pgrsSetUploadCounter() - amount of data currently uploaded
   pgrsUpdate() - show progress
   pgrsDone() - transfer complete

*/
#if 1
void pgrsDone(struct UrlData *data)
{
  if(!(data->progress.flags & PGRS_HIDE)) {
    data->progress.lastshow=0;
    pgrsUpdate(data); /* the final (forced) update */
    fprintf(stderr, "\n");
  }
}
void pgrsMode(struct UrlData *data, int mode)
{
  /* mode should include a hidden mode as well */
  if(data->conf&(CONF_NOPROGRESS|CONF_MUTE))
    data->progress.flags |= PGRS_HIDE; /* don't show anything */
  else {
    data->progress.mode = mode; /* store type */
  }

}

void pgrsTime(struct UrlData *data, timerid timer)
{
  switch(timer) {
  default:
  case TIMER_NONE:
    /* mistake filter */
    break;
  case TIMER_NAMELOOKUP:
    data->progress.t_nslookup = tvnow();
    break;
  case TIMER_CONNECT:
    data->progress.t_connect = tvnow();
    break;
  case TIMER_PRETRANSFER:
    data->progress.t_pretransfer = tvnow();
    break;
  case TIMER_POSTRANSFER:
    /* this is the normal end-of-transfer thing */
    break;
  }
}

void pgrsStartNow(struct UrlData *data)
{
  data->progress.start = tvnow();
}

void pgrsSetDownloadCounter(struct UrlData *data, double size)
{
  data->progress.downloaded = size;
}

void pgrsSetUploadCounter(struct UrlData *data, double size)
{
  data->progress.uploaded = size;
}

void pgrsSetDownloadSize(struct UrlData *data, double size)
{
  if(size > 0) {
    data->progress.size_dl = size;
    data->progress.flags |= PGRS_DL_SIZE_KNOWN;
  }
}

void pgrsSetUploadSize(struct UrlData *data, double size)
{
  if(size > 0) {
    data->progress.size_ul = size;
    data->progress.flags |= PGRS_UL_SIZE_KNOWN;
  }
}

/* EXAMPLE OUTPUT to follow:

  % Total    % Received % Xferd  Average Speed          Time             Curr.
                                 Dload  Upload Total    Current  Left    Speed
100 12345  100 12345  100 12345  12345  12345 12:12:12 12:12:12 12:12:12 12345

 */

void pgrsUpdate(struct UrlData *data)
{
  struct timeval now;

  if(data->progress.flags & PGRS_HIDE)
    ; /* We do enter this function even if we don't wanna see anything, since
         this is were lots of the calculations are being made that will be used
         even when not displayed! */
  else if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
    if ( data->progress.mode == CURL_PROGRESS_STATS ) {
      fprintf(data->err,
              "  %% Total    %% Received %% Xferd  Average Speed          Time             Curr.\n"
              "                                 Dload  Upload Total    Current  Left    Speed\n");
    }
    data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */
  }

  now = tvnow(); /* what time is it */

  switch(data->progress.mode) {
  case CURL_PROGRESS_STATS:
  default:
    {
      char max5[6][6];
      double dlpercen=0;
      double ulpercen=0;
      double total_percen=0;

      double total_transfer;
      double total_expected_transfer;

#define CURR_TIME 5

      static double speeder[ CURR_TIME ];
      static int speeder_c=0;

      int nowindex = speeder_c% CURR_TIME;
      int checkindex;
      int count;

      char time_left[10];
      char time_total[10];
      char time_current[10];

      double ulestimate=0;
      double dlestimate=0;
          
      double total_estimate;

      if(data->progress.lastshow == tvlong(now))
        return; /* never update this more than once a second if the end isn't 
                   reached */
      data->progress.lastshow = now.tv_sec;

      /* The exact time spent so far */
      data->progress.timespent = tvdiff (now, data->progress.start);

      /* The average download speed this far */
      data->progress.dlspeed = data->progress.downloaded/(data->progress.timespent!=0.0?data->progress.timespent:1.0);

      /* The average upload speed this far */
      data->progress.ulspeed = data->progress.uploaded/(data->progress.timespent!=0.0?data->progress.timespent:1.0);

      /* Let's do the "current speed" thing, which should use the fastest
         of the dl/ul speeds */

      speeder[ nowindex ] = data->progress.downloaded>data->progress.uploaded?
        data->progress.downloaded:data->progress.uploaded;
      speeder_c++; /* increase */
      count = ((speeder_c>=CURR_TIME)?CURR_TIME:speeder_c) - 1;
      checkindex = (speeder_c>=CURR_TIME)?speeder_c%CURR_TIME:0;

      /* find out the average speed the last CURR_TIME seconds */
      data->progress.current_speed =
        (speeder[nowindex]-speeder[checkindex])/(count?count:1);

      if(data->progress.flags & PGRS_HIDE)
        return;

      /* Figure out the estimated time of arrival for the upload */
      if(data->progress.flags & PGRS_UL_SIZE_KNOWN) {
        if(!data->progress.ulspeed)
          data->progress.ulspeed=1;
        ulestimate = data->progress.size_ul / data->progress.ulspeed;
        ulpercen = (data->progress.uploaded / data->progress.size_ul)*100;
      }

      /* ... and the download */
      if(data->progress.flags & PGRS_DL_SIZE_KNOWN) {
        if(!data->progress.dlspeed)
          data->progress.dlspeed=1;
        dlestimate = data->progress.size_dl / data->progress.dlspeed;
        dlpercen = (data->progress.downloaded / data->progress.size_dl)*100;
      }
    
      /* Now figure out which of them that is slower and use for the for
         total estimate! */
      total_estimate = ulestimate>dlestimate?ulestimate:dlestimate;

      /* If we have a total estimate, we can display that and the expected
         time left */
      if(total_estimate) {
        time2str(time_left, total_estimate-(int) data->progress.timespent); 
        time2str(time_total, total_estimate);
      }
      else {
        /* otherwise we blank those times */
        strcpy(time_left,  "--:--:--");
        strcpy(time_total, "--:--:--");
      }
      /* The time spent so far is always known */
      time2str(time_current, data->progress.timespent);

      /* Get the total amount of data expected to get transfered */
      total_expected_transfer = 
        (data->progress.flags & PGRS_UL_SIZE_KNOWN?
         data->progress.size_ul:data->progress.uploaded)+
        (data->progress.flags & PGRS_DL_SIZE_KNOWN?
         data->progress.size_dl:data->progress.downloaded);
      
      /* We have transfered this much so far */
      total_transfer = data->progress.downloaded + data->progress.uploaded;

      /* Get the percentage of data transfered so far */
      if(total_expected_transfer)
        total_percen=(double)(total_transfer/total_expected_transfer)*100;


      fprintf(stderr,
              "\r%3d %s  %3d %s  %3d %s  %s  %s %s %s %s %s",
              (int)total_percen,                            /* total % */
              max5data(total_expected_transfer, max5[2]),   /* total size */
              (int)dlpercen,                                /* rcvd % */
              max5data(data->progress.downloaded, max5[0]), /* rcvd size */
              (int)ulpercen,                                /* xfer % */
              max5data(data->progress.uploaded, max5[1]),   /* xfer size */

              max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */
              max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */
              time_total,                           /* total time */
              time_current,                         /* current time */
              time_left,                            /* time left */
              max5data(data->progress.current_speed, max5[5]) /* current speed */
              );
    }
    break;
#if 0
  case CURL_PROGRESS_BAR:
    /* original progress bar code by Lars Aas */
    if (progressmax == -1) {
      int prevblock = prev / 1024;
      int thisblock = point / 1024;
      while ( thisblock > prevblock ) {
        fprintf( data->err, "#" );
        prevblock++;
      }
        prev = point;
    }
    else {
      char line[256];
      char outline[256];
      char format[40];
      float frac = (float) point / (float) progressmax;
      float percent = frac * 100.0f;
      int barwidth = width - 7;
      int num = (int) (((float)barwidth) * frac);
        int i = 0;
        for ( i = 0; i < num; i++ ) {
          line[i] = '#';
        }
        line[i] = '\0';
        sprintf( format, "%%-%ds %%5.1f%%%%", barwidth );
        sprintf( outline, format, line, percent );
        fprintf( data->err, "\r%s", outline );
    }
    prev = point;
    break;
#endif
  }
}


#endif

#if 0
/* --- start of (the former) progress routines --- */
int progressmax=-1;

static int prev = 0;
static int width = 0;

void ProgressInit(struct UrlData *data, int max/*, int options, int moremax*/)
{
#ifdef __EMX__
  /* 20000318 mgs */
  int scr_size [2];
#endif

  if(data->conf&(CONF_NOPROGRESS|CONF_MUTE))
    return;

  prev = 0;

/* TODO: get terminal width through ansi escapes or something similar.
         try to update width when xterm is resized... - 19990617 larsa */
#ifndef __EMX__
  /* 20000318 mgs
   * OS/2 users most likely won't have this env var set, and besides that
   * we're using our own way to determine screen width */
  if (curl_GetEnv("COLUMNS") != NULL)
    width = atoi(curl_GetEnv("COLUMNS"));
  else
    width = 79;
#else
  /* 20000318 mgs
   * We use this emx library call to get the screen width, and subtract
   * one from what we got in order to avoid a problem with the cursor
   * advancing to the next line if we print a string that is as long as
   * the screen is wide. */
 
  _scrsize(scr_size);
  width = scr_size[0] - 1;
#endif


  progressmax = max;
  if(-1 == max)
    return;
  if(progressmax <= LEAST_SIZE_PROGRESS) {
    progressmax = -1; /* disable */
    return;
  }

  if ( data->progressmode == CURL_PROGRESS_STATS )
    fprintf(data->err,
            "  %%   Received    Total    Speed  Estimated   Time      Left   Curr.Speed\n");

}

void ProgressShow(struct UrlData *data,
                  int point, struct timeval start, struct timeval now, bool force)
{
  switch ( data->progressmode ) {
  case CURL_PROGRESS_STATS:
    {
      static long lastshow;
      double percen;

      double spent;
      double speed;

#define CURR_TIME 5

      static int speeder[ CURR_TIME ];
      static int speeder_c=0;

      int nowindex = speeder_c% CURR_TIME;
      int checkindex;
      int count;

      if(!force && (point != progressmax) && (lastshow == tvlong(now)))
        return; /* never update this more than once a second if the end isn't 
                   reached */

      spent = tvdiff (now, start);
      speed = point/(spent!=0.0?spent:1.0);
      if(!speed)
        speed=1;

      /* point is where we are right now */
      speeder[ nowindex ] = point;
      speeder_c++; /* increase */
      count = ((speeder_c>=CURR_TIME)?CURR_TIME:speeder_c) - 1;
      checkindex = (speeder_c>=CURR_TIME)?speeder_c%CURR_TIME:0;

      /* find out the average speed the last CURR_TIME seconds */
      data->current_speed = (speeder[nowindex]-speeder[checkindex])/(count?count:1);

#if 0
      printf("NOW %d(%d) THEN %d(%d) DIFF %lf COUNT %d\n",
	     speeder[nowindex], nowindex,
	     speeder[checkindex], checkindex,
	     data->current_speed, count);
#endif

      if(data->conf&(CONF_NOPROGRESS|CONF_MUTE))
        return;

      if(-1 != progressmax) {
        char left[20];
        char estim[20];
        char timespent[20];
        int estimate = progressmax/(int) speed;
    
        time2str(left,estimate-(int) spent); 
        time2str(estim,estimate);
        time2str(timespent,spent);

        percen=(double)point/progressmax;
        percen=percen*100;

        fprintf(stderr, "\r%3d %10d %10d %6.0lf %s %s %s %6.0lf   ",
                (int)percen, point, progressmax,
                speed, estim, timespent, left, data->current_speed);
      }
      else
        fprintf(data->err,
                "\r%d bytes received in %.3lf seconds (%.0lf bytes/sec)",
                point, spent, speed);

      lastshow = now.tv_sec;
      break;
    }
  case CURL_PROGRESS_BAR: /* 19990617 larsa */
    {
      if (point == prev) break;
      if (progressmax == -1) {
        int prevblock = prev / 1024;
        int thisblock = point / 1024;
        while ( thisblock > prevblock ) {
            fprintf( data->err, "#" );
            prevblock++;
        }
        prev = point;
      } else {
        char line[256];
        char outline[256];
        char format[40];
        float frac = (float) point / (float) progressmax;
        float percent = frac * 100.0f;
        int barwidth = width - 7;
        int num = (int) (((float)barwidth) * frac);
        int i = 0;
        for ( i = 0; i < num; i++ ) {
            line[i] = '#';
        }
        line[i] = '\0';
        sprintf( format, "%%-%ds %%5.1f%%%%", barwidth );
        sprintf( outline, format, line, percent );
        fprintf( data->err, "\r%s", outline );
      }
      prev = point;
      break;
    }
  default: /* 19990617 larsa */
    {
      int prevblock = prev / 1024;
      int thisblock = point / 1024;
      if (prev == point) break;
      while ( thisblock > prevblock ) {
        fprintf( data->err, "#" );
        prevblock++;
      }
      prev = point;
      break;
    }
  }
}

void ProgressEnd(struct UrlData *data)
{
  if(data->conf&(CONF_NOPROGRESS|CONF_MUTE))
    return;
  fputs("\n", data->err);
}

/* --- end of progress routines --- */
#endif