moved here from the newlib branch
[platform/upstream/curl.git] / lib / progress.c
1 /*****************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  *  The contents of this file are subject to the Mozilla Public License
9  *  Version 1.0 (the "License"); you may not use this file except in
10  *  compliance with the License. You may obtain a copy of the License at
11  *  http://www.mozilla.org/MPL/
12  *
13  *  Software distributed under the License is distributed on an "AS IS"
14  *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
15  *  License for the specific language governing rights and limitations
16  *  under the License.
17  *
18  *  The Original Code is Curl.
19  *
20  *  The Initial Developer of the Original Code is Daniel Stenberg.
21  *
22  *  Portions created by the Initial Developer are Copyright (C) 1998.
23  *  All Rights Reserved.
24  *
25  * ------------------------------------------------------------
26  * Main author:
27  * - Daniel Stenberg <Daniel.Stenberg@haxx.nu>
28  *
29  *      http://curl.haxx.nu
30  *
31  * $Source$
32  * $Revision$
33  * $Date$
34  * $Author$
35  * $State$
36  * $Locker$
37  *
38  * ------------------------------------------------------------
39  ****************************************************************************/
40
41 #include <string.h>
42 #include "setup.h"
43
44 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
45 #if defined(__MINGW32__)
46 #include <winsock.h>
47 #endif
48 #include <time.h>
49 #endif
50
51 /* 20000318 mgs
52  * later we use _scrsize to determine the screen width, this emx library
53  * function needs stdlib.h to be included */
54 #if defined(__EMX__)
55 #include <stdlib.h>
56 #endif
57
58 #include <curl/curl.h>
59 #include "urldata.h"
60
61 #include "progress.h"
62
63 void time2str(char *r, int t)
64 {
65   int h = (t/3600);
66   int m = (t-(h*3600))/60;
67   int s = (t-(h*3600)-(m*60));
68   sprintf(r,"%2d:%02d:%02d",h,m,s);
69 }
70
71 /* The point of this function would be to return a string of the input data,
72    but never longer than 5 columns. Add suffix k, M, G when suitable... */
73 char *max5data(double bytes, char *max5)
74 {
75 #define ONE_KILOBYTE 1024
76 #define ONE_MEGABYTE (1024*1024)
77
78   if(bytes < 100000) {
79     sprintf(max5, "%5d", (int)bytes);
80     return max5;
81   }
82   if(bytes < (9999*ONE_KILOBYTE)) {
83     sprintf(max5, "%4dk", (int)bytes/ONE_KILOBYTE);
84     return max5;
85   }
86   if(bytes < (100*ONE_MEGABYTE)) {
87     /* 'XX.XM' is good as long as we're less than 100 megs */
88     sprintf(max5, "%2.1fM", bytes/ONE_MEGABYTE);
89     return max5;
90   }
91   sprintf(max5, "%4dM", (int)bytes/ONE_MEGABYTE);
92   return max5;
93 }
94
95 /* 
96
97    New proposed interface, 9th of February 2000:
98
99    pgrsStartNow() - sets start time
100    pgrsMode(type) - kind of display
101    pgrsSetDownloadSize(x) - known expected download size
102    pgrsSetUploadSize(x) - known expected upload size
103    pgrsSetDownloadCounter() - amount of data currently downloaded
104    pgrsSetUploadCounter() - amount of data currently uploaded
105    pgrsUpdate() - show progress
106    pgrsDone() - transfer complete
107
108 */
109 #if 1
110 void pgrsDone(struct UrlData *data)
111 {
112   if(!(data->progress.flags & PGRS_HIDE)) {
113     data->progress.lastshow=0;
114     pgrsUpdate(data); /* the final (forced) update */
115     fprintf(stderr, "\n");
116   }
117 }
118 void pgrsMode(struct UrlData *data, int mode)
119 {
120   /* mode should include a hidden mode as well */
121   if(data->bits.hide_progress || data->bits.mute)
122     data->progress.flags |= PGRS_HIDE; /* don't show anything */
123   else {
124     data->progress.mode = mode; /* store type */
125   }
126
127 }
128
129 void pgrsTime(struct UrlData *data, timerid timer)
130 {
131   switch(timer) {
132   default:
133   case TIMER_NONE:
134     /* mistake filter */
135     break;
136   case TIMER_NAMELOOKUP:
137     data->progress.t_nslookup = tvnow();
138     break;
139   case TIMER_CONNECT:
140     data->progress.t_connect = tvnow();
141     break;
142   case TIMER_PRETRANSFER:
143     data->progress.t_pretransfer = tvnow();
144     break;
145   case TIMER_POSTRANSFER:
146     /* this is the normal end-of-transfer thing */
147     break;
148   }
149 }
150
151 void pgrsStartNow(struct UrlData *data)
152 {
153   data->progress.start = tvnow();
154 }
155
156 void pgrsSetDownloadCounter(struct UrlData *data, double size)
157 {
158   data->progress.downloaded = size;
159 }
160
161 void pgrsSetUploadCounter(struct UrlData *data, double size)
162 {
163   data->progress.uploaded = size;
164 }
165
166 void pgrsSetDownloadSize(struct UrlData *data, double size)
167 {
168   if(size > 0) {
169     data->progress.size_dl = size;
170     data->progress.flags |= PGRS_DL_SIZE_KNOWN;
171   }
172 }
173
174 void pgrsSetUploadSize(struct UrlData *data, double size)
175 {
176   if(size > 0) {
177     data->progress.size_ul = size;
178     data->progress.flags |= PGRS_UL_SIZE_KNOWN;
179   }
180 }
181
182 /* EXAMPLE OUTPUT to follow:
183
184   % Total    % Received % Xferd  Average Speed          Time             Curr.
185                                  Dload  Upload Total    Current  Left    Speed
186 100 12345  100 12345  100 12345  12345  12345 12:12:12 12:12:12 12:12:12 12345
187
188  */
189
190 int pgrsUpdate(struct UrlData *data)
191 {
192   struct timeval now;
193
194   char max5[6][6];
195   double dlpercen=0;
196   double ulpercen=0;
197   double total_percen=0;
198
199   double total_transfer;
200   double total_expected_transfer;
201
202 #define CURR_TIME 5
203
204   static double speeder[ CURR_TIME ];
205   static int speeder_c=0;
206
207   int nowindex = speeder_c% CURR_TIME;
208   int checkindex;
209   int count;
210
211   char time_left[10];
212   char time_total[10];
213   char time_current[10];
214       
215   double ulestimate=0;
216   double dlestimate=0;
217   
218   double total_estimate;
219
220   if(data->progress.flags & PGRS_HIDE)
221     ; /* We do enter this function even if we don't wanna see anything, since
222          this is were lots of the calculations are being made that will be used
223          even when not displayed! */
224   else if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
225     if ( data->progress.mode == CURL_PROGRESS_STATS ) {
226       fprintf(data->err,
227               "  %% Total    %% Received %% Xferd  Average Speed          Time             Curr.\n"
228               "                                 Dload  Upload Total    Current  Left    Speed\n");
229     }
230     data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */
231   }
232
233   now = tvnow(); /* what time is it */
234
235   if(data->progress.lastshow == tvlong(now))
236     return 0; /* never update this more than once a second if the end isn't 
237                  reached */
238   data->progress.lastshow = now.tv_sec;
239
240   /* The exact time spent so far */
241   data->progress.timespent = tvdiff (now, data->progress.start);
242
243   /* The average download speed this far */
244   data->progress.dlspeed = data->progress.downloaded/(data->progress.timespent!=0.0?data->progress.timespent:1.0);
245
246   /* The average upload speed this far */
247   data->progress.ulspeed = data->progress.uploaded/(data->progress.timespent!=0.0?data->progress.timespent:1.0);
248
249   /* Let's do the "current speed" thing, which should use the fastest
250          of the dl/ul speeds */
251
252   speeder[ nowindex ] = data->progress.downloaded>data->progress.uploaded?
253     data->progress.downloaded:data->progress.uploaded;
254   speeder_c++; /* increase */
255   count = ((speeder_c>=CURR_TIME)?CURR_TIME:speeder_c) - 1;
256   checkindex = (speeder_c>=CURR_TIME)?speeder_c%CURR_TIME:0;
257
258   /* find out the average speed the last CURR_TIME seconds */
259   data->progress.current_speed =
260     (speeder[nowindex]-speeder[checkindex])/(count?count:1);
261
262   if(data->progress.flags & PGRS_HIDE)
263     return 0;
264   else if(data->fprogress) {
265     return data->fprogress(data->progress_client,
266                            data->progress.size_dl,
267                            data->progress.downloaded,
268                            data->progress.size_ul,
269                            data->progress.uploaded);
270   }
271
272       /* Figure out the estimated time of arrival for the upload */
273   if(data->progress.flags & PGRS_UL_SIZE_KNOWN) {
274     if(!data->progress.ulspeed)
275       data->progress.ulspeed=1;
276     ulestimate = data->progress.size_ul / data->progress.ulspeed;
277     ulpercen = (data->progress.uploaded / data->progress.size_ul)*100;
278   }
279
280   /* ... and the download */
281   if(data->progress.flags & PGRS_DL_SIZE_KNOWN) {
282     if(!data->progress.dlspeed)
283       data->progress.dlspeed=1;
284     dlestimate = data->progress.size_dl / data->progress.dlspeed;
285     dlpercen = (data->progress.downloaded / data->progress.size_dl)*100;
286   }
287     
288   /* Now figure out which of them that is slower and use for the for
289          total estimate! */
290   total_estimate = ulestimate>dlestimate?ulestimate:dlestimate;
291
292   /* If we have a total estimate, we can display that and the expected
293          time left */
294   if(total_estimate) {
295     time2str(time_left, total_estimate-(int) data->progress.timespent); 
296     time2str(time_total, total_estimate);
297   }
298   else {
299     /* otherwise we blank those times */
300     strcpy(time_left,  "--:--:--");
301     strcpy(time_total, "--:--:--");
302   }
303   /* The time spent so far is always known */
304   time2str(time_current, data->progress.timespent);
305
306   /* Get the total amount of data expected to get transfered */
307   total_expected_transfer = 
308     (data->progress.flags & PGRS_UL_SIZE_KNOWN?
309      data->progress.size_ul:data->progress.uploaded)+
310     (data->progress.flags & PGRS_DL_SIZE_KNOWN?
311      data->progress.size_dl:data->progress.downloaded);
312       
313   /* We have transfered this much so far */
314   total_transfer = data->progress.downloaded + data->progress.uploaded;
315
316   /* Get the percentage of data transfered so far */
317   if(total_expected_transfer)
318     total_percen=(double)(total_transfer/total_expected_transfer)*100;
319
320   fprintf(stderr,
321           "\r%3d %s  %3d %s  %3d %s  %s  %s %s %s %s %s",
322           (int)total_percen,                            /* total % */
323           max5data(total_expected_transfer, max5[2]),   /* total size */
324           (int)dlpercen,                                /* rcvd % */
325           max5data(data->progress.downloaded, max5[0]), /* rcvd size */
326           (int)ulpercen,                                /* xfer % */
327           max5data(data->progress.uploaded, max5[1]),   /* xfer size */
328
329           max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */
330           max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */
331           time_total,                           /* total time */
332           time_current,                         /* current time */
333           time_left,                            /* time left */
334           max5data(data->progress.current_speed, max5[5]) /* current speed */
335           );
336
337
338 #if 0
339   case CURL_PROGRESS_BAR:
340     /* original progress bar code by Lars Aas */
341     if (progressmax == -1) {
342       int prevblock = prev / 1024;
343       int thisblock = point / 1024;
344       while ( thisblock > prevblock ) {
345         fprintf( data->err, "#" );
346         prevblock++;
347       }
348         prev = point;
349     }
350     else {
351       char line[256];
352       char outline[256];
353       char format[40];
354       float frac = (float) point / (float) progressmax;
355       float percent = frac * 100.0f;
356       int barwidth = width - 7;
357       int num = (int) (((float)barwidth) * frac);
358         int i = 0;
359         for ( i = 0; i < num; i++ ) {
360           line[i] = '#';
361         }
362         line[i] = '\0';
363         sprintf( format, "%%-%ds %%5.1f%%%%", barwidth );
364         sprintf( outline, format, line, percent );
365         fprintf( data->err, "\r%s", outline );
366     }
367     prev = point;
368     break;
369 #endif
370
371     return 0;
372 }
373
374
375 #endif
376
377 #if 0
378 /* --- start of (the former) progress routines --- */
379 int progressmax=-1;
380
381 static int prev = 0;
382 static int width = 0;
383
384 void ProgressInit(struct UrlData *data, int max/*, int options, int moremax*/)
385 {
386 #ifdef __EMX__
387   /* 20000318 mgs */
388   int scr_size [2];
389 #endif
390
391   if(data->conf&(CONF_NOPROGRESS|CONF_MUTE))
392     return;
393
394   prev = 0;
395
396 /* TODO: get terminal width through ansi escapes or something similar.
397          try to update width when xterm is resized... - 19990617 larsa */
398 #ifndef __EMX__
399   /* 20000318 mgs
400    * OS/2 users most likely won't have this env var set, and besides that
401    * we're using our own way to determine screen width */
402   if (curl_GetEnv("COLUMNS") != NULL)
403     width = atoi(curl_GetEnv("COLUMNS"));
404   else
405     width = 79;
406 #else
407   /* 20000318 mgs
408    * We use this emx library call to get the screen width, and subtract
409    * one from what we got in order to avoid a problem with the cursor
410    * advancing to the next line if we print a string that is as long as
411    * the screen is wide. */
412  
413   _scrsize(scr_size);
414   width = scr_size[0] - 1;
415 #endif
416
417
418   progressmax = max;
419   if(-1 == max)
420     return;
421   if(progressmax <= LEAST_SIZE_PROGRESS) {
422     progressmax = -1; /* disable */
423     return;
424   }
425
426   if ( data->progressmode == CURL_PROGRESS_STATS )
427     fprintf(data->err,
428             "  %%   Received    Total    Speed  Estimated   Time      Left   Curr.Speed\n");
429
430 }
431
432 void ProgressShow(struct UrlData *data,
433                   int point, struct timeval start, struct timeval now, bool force)
434 {
435   switch ( data->progressmode ) {
436   case CURL_PROGRESS_STATS:
437     {
438       static long lastshow;
439       double percen;
440
441       double spent;
442       double speed;
443
444 #define CURR_TIME 5
445
446       static int speeder[ CURR_TIME ];
447       static int speeder_c=0;
448
449       int nowindex = speeder_c% CURR_TIME;
450       int checkindex;
451       int count;
452
453       if(!force && (point != progressmax) && (lastshow == tvlong(now)))
454         return; /* never update this more than once a second if the end isn't 
455                    reached */
456
457       spent = tvdiff (now, start);
458       speed = point/(spent!=0.0?spent:1.0);
459       if(!speed)
460         speed=1;
461
462       /* point is where we are right now */
463       speeder[ nowindex ] = point;
464       speeder_c++; /* increase */
465       count = ((speeder_c>=CURR_TIME)?CURR_TIME:speeder_c) - 1;
466       checkindex = (speeder_c>=CURR_TIME)?speeder_c%CURR_TIME:0;
467
468       /* find out the average speed the last CURR_TIME seconds */
469       data->current_speed = (speeder[nowindex]-speeder[checkindex])/(count?count:1);
470
471 #if 0
472       printf("NOW %d(%d) THEN %d(%d) DIFF %lf COUNT %d\n",
473              speeder[nowindex], nowindex,
474              speeder[checkindex], checkindex,
475              data->current_speed, count);
476 #endif
477
478       if(data->conf&(CONF_NOPROGRESS|CONF_MUTE))
479         return;
480
481       if(-1 != progressmax) {
482         char left[20];
483         char estim[20];
484         char timespent[20];
485         int estimate = progressmax/(int) speed;
486     
487         time2str(left,estimate-(int) spent); 
488         time2str(estim,estimate);
489         time2str(timespent,spent);
490
491         percen=(double)point/progressmax;
492         percen=percen*100;
493
494         fprintf(stderr, "\r%3d %10d %10d %6.0lf %s %s %s %6.0lf   ",
495                 (int)percen, point, progressmax,
496                 speed, estim, timespent, left, data->current_speed);
497       }
498       else
499         fprintf(data->err,
500                 "\r%d bytes received in %.3lf seconds (%.0lf bytes/sec)",
501                 point, spent, speed);
502
503       lastshow = now.tv_sec;
504       break;
505     }
506   case CURL_PROGRESS_BAR: /* 19990617 larsa */
507     {
508       if (point == prev) break;
509       if (progressmax == -1) {
510         int prevblock = prev / 1024;
511         int thisblock = point / 1024;
512         while ( thisblock > prevblock ) {
513             fprintf( data->err, "#" );
514             prevblock++;
515         }
516         prev = point;
517       } else {
518         char line[256];
519         char outline[256];
520         char format[40];
521         float frac = (float) point / (float) progressmax;
522         float percent = frac * 100.0f;
523         int barwidth = width - 7;
524         int num = (int) (((float)barwidth) * frac);
525         int i = 0;
526         for ( i = 0; i < num; i++ ) {
527             line[i] = '#';
528         }
529         line[i] = '\0';
530         sprintf( format, "%%-%ds %%5.1f%%%%", barwidth );
531         sprintf( outline, format, line, percent );
532         fprintf( data->err, "\r%s", outline );
533       }
534       prev = point;
535       break;
536     }
537   default: /* 19990617 larsa */
538     {
539       int prevblock = prev / 1024;
540       int thisblock = point / 1024;
541       if (prev == point) break;
542       while ( thisblock > prevblock ) {
543         fprintf( data->err, "#" );
544         prevblock++;
545       }
546       prev = point;
547       break;
548     }
549   }
550 }
551
552 void ProgressEnd(struct UrlData *data)
553 {
554   if(data->conf&(CONF_NOPROGRESS|CONF_MUTE))
555     return;
556   fputs("\n", data->err);
557 }
558
559 /* --- end of progress routines --- */
560 #endif