data->err must be used, not stderr
[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@haxx.se>
28  *
29  *      http://curl.haxx.se
30  *
31  * $Source$
32  * $Revision$
33  * $Date$
34  * $Author$
35  * $State$
36  * $Locker$
37  *
38  * ------------------------------------------------------------
39  ****************************************************************************/
40
41 #include "setup.h"
42
43 #include <string.h>
44
45 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
46 #if defined(__MINGW32__)
47 #include <winsock.h>
48 #endif
49 #include <time.h>
50 #endif
51
52 /* 20000318 mgs
53  * later we use _scrsize to determine the screen width, this emx library
54  * function needs stdlib.h to be included */
55 #if defined(__EMX__)
56 #include <stdlib.h>
57 #endif
58
59 #include <curl/curl.h>
60 #include "urldata.h"
61 #include "sendf.h"
62
63 #include "progress.h"
64
65 void time2str(char *r, int t)
66 {
67   int h = (t/3600);
68   int m = (t-(h*3600))/60;
69   int s = (t-(h*3600)-(m*60));
70   sprintf(r,"%2d:%02d:%02d",h,m,s);
71 }
72
73 /* The point of this function would be to return a string of the input data,
74    but never longer than 5 columns. Add suffix k, M, G when suitable... */
75 char *max5data(double bytes, char *max5)
76 {
77 #define ONE_KILOBYTE 1024
78 #define ONE_MEGABYTE (1024*1024)
79
80   if(bytes < 100000) {
81     sprintf(max5, "%5d", (int)bytes);
82     return max5;
83   }
84   if(bytes < (9999*ONE_KILOBYTE)) {
85     sprintf(max5, "%4dk", (int)bytes/ONE_KILOBYTE);
86     return max5;
87   }
88   if(bytes < (100*ONE_MEGABYTE)) {
89     /* 'XX.XM' is good as long as we're less than 100 megs */
90     sprintf(max5, "%2.1fM", bytes/ONE_MEGABYTE);
91     return max5;
92   }
93   sprintf(max5, "%4dM", (int)bytes/ONE_MEGABYTE);
94   return max5;
95 }
96
97 /* 
98
99    New proposed interface, 9th of February 2000:
100
101    pgrsStartNow() - sets start time
102    pgrsSetDownloadSize(x) - known expected download size
103    pgrsSetUploadSize(x) - known expected upload size
104    pgrsSetDownloadCounter() - amount of data currently downloaded
105    pgrsSetUploadCounter() - amount of data currently uploaded
106    pgrsUpdate() - show progress
107    pgrsDone() - transfer complete
108
109 */
110
111 void pgrsDone(struct UrlData *data)
112 {
113   if(!(data->progress.flags & PGRS_HIDE)) {
114     data->progress.lastshow=0;
115     pgrsUpdate(data); /* the final (forced) update */
116     fprintf(data->err, "\n");
117   }
118 }
119
120 void pgrsTime(struct UrlData *data, timerid timer)
121 {
122   switch(timer) {
123   default:
124   case TIMER_NONE:
125     /* mistake filter */
126     break;
127   case TIMER_STARTSINGLE:
128     /* This is set at the start of a single fetch, there may be several
129        fetches within an operation, why we add all other times relative
130        to this one */
131     data->progress.t_startsingle = tvnow();
132     break;
133
134   case TIMER_NAMELOOKUP:
135     data->progress.t_nslookup += tvdiff(tvnow(),
136                                         data->progress.t_startsingle);
137     break;
138   case TIMER_CONNECT:
139     data->progress.t_connect += tvdiff(tvnow(),
140                                        data->progress.t_startsingle);
141     break;
142   case TIMER_PRETRANSFER:
143     data->progress.t_pretransfer += tvdiff(tvnow(),
144                                            data->progress.t_startsingle);
145     break;
146   case TIMER_POSTRANSFER:
147     /* this is the normal end-of-transfer thing */
148     break;
149   }
150 }
151
152 void pgrsStartNow(struct UrlData *data)
153 {
154   data->progress.start = tvnow();
155 }
156
157 void pgrsSetDownloadCounter(struct UrlData *data, double size)
158 {
159   data->progress.downloaded = size;
160 }
161
162 void pgrsSetUploadCounter(struct UrlData *data, double size)
163 {
164   data->progress.uploaded = size;
165 }
166
167 void pgrsSetDownloadSize(struct UrlData *data, double size)
168 {
169   if(size > 0) {
170     data->progress.size_dl = size;
171     data->progress.flags |= PGRS_DL_SIZE_KNOWN;
172   }
173 }
174
175 void pgrsSetUploadSize(struct UrlData *data, double size)
176 {
177   if(size > 0) {
178     data->progress.size_ul = size;
179     data->progress.flags |= PGRS_UL_SIZE_KNOWN;
180   }
181 }
182
183 /* EXAMPLE OUTPUT to follow:
184
185   % Total    % Received % Xferd  Average Speed          Time             Curr.
186                                  Dload  Upload Total    Current  Left    Speed
187 100 12345  100 12345  100 12345  12345  12345 12:12:12 12:12:12 12:12:12 12345
188
189  */
190
191 int pgrsUpdate(struct UrlData *data)
192 {
193   struct timeval now;
194   int result;
195
196   char max5[6][10];
197   double dlpercen=0;
198   double ulpercen=0;
199   double total_percen=0;
200
201   double total_transfer;
202   double total_expected_transfer;
203
204   int nowindex = data->progress.speeder_c% CURR_TIME;
205   int checkindex;
206   int count;
207
208   char time_left[10];
209   char time_total[10];
210   char time_current[10];
211       
212   double ulestimate=0;
213   double dlestimate=0;
214   
215   double total_estimate;
216
217   if(data->progress.flags & PGRS_HIDE)
218     ; /* We do enter this function even if we don't wanna see anything, since
219          this is were lots of the calculations are being made that will be used
220          even when not displayed! */
221   else if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
222     if (!data->progress.callback) {
223       fprintf(data->err,
224               "  %% Total    %% Received %% Xferd  Average Speed          Time             Curr.\n"
225               "                                 Dload  Upload Total    Current  Left    Speed\n");
226     }
227     data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */
228   }
229
230   now = tvnow(); /* what time is it */
231
232   if(data->progress.lastshow == tvlong(now))
233     return 0; /* never update this more than once a second if the end isn't 
234                  reached */
235   data->progress.lastshow = now.tv_sec;
236
237   /* The exact time spent so far */
238   data->progress.timespent = tvdiff (now, data->progress.start);
239
240   /* The average download speed this far */
241   data->progress.dlspeed = data->progress.downloaded/(data->progress.timespent!=0.0?data->progress.timespent:1.0);
242
243   /* The average upload speed this far */
244   data->progress.ulspeed = data->progress.uploaded/(data->progress.timespent!=0.0?data->progress.timespent:1.0);
245
246   /* Let's do the "current speed" thing, which should use the fastest
247          of the dl/ul speeds */
248
249   data->progress.speeder[ nowindex ] =
250     data->progress.downloaded>data->progress.uploaded?
251     data->progress.downloaded:data->progress.uploaded;
252   data->progress.speeder_c++; /* increase */
253   count = ((data->progress.speeder_c>=CURR_TIME)?
254            CURR_TIME:data->progress.speeder_c) - 1;
255   checkindex = (data->progress.speeder_c>=CURR_TIME)?
256     data->progress.speeder_c%CURR_TIME:0;
257
258   /* find out the average speed the last CURR_TIME seconds */
259   data->progress.current_speed =
260     (data->progress.speeder[nowindex]-
261      data->progress.speeder[checkindex])/(count?count:1);
262
263   if(data->progress.flags & PGRS_HIDE)
264     return 0;
265   else if(data->fprogress) {
266     result= data->fprogress(data->progress_client,
267                             data->progress.size_dl,
268                             data->progress.downloaded,
269                             data->progress.size_ul,
270                             data->progress.uploaded);
271     if(result)
272       failf(data, "Callback aborted");
273     return result;
274   }
275
276       /* Figure out the estimated time of arrival for the upload */
277   if(data->progress.flags & PGRS_UL_SIZE_KNOWN) {
278     if(!data->progress.ulspeed)
279       data->progress.ulspeed=1;
280     ulestimate = data->progress.size_ul / data->progress.ulspeed;
281     ulpercen = (data->progress.uploaded / data->progress.size_ul)*100;
282   }
283
284   /* ... and the download */
285   if(data->progress.flags & PGRS_DL_SIZE_KNOWN) {
286     if(!data->progress.dlspeed)
287       data->progress.dlspeed=1;
288     dlestimate = data->progress.size_dl / data->progress.dlspeed;
289     dlpercen = (data->progress.downloaded / data->progress.size_dl)*100;
290   }
291     
292   /* Now figure out which of them that is slower and use for the for
293          total estimate! */
294   total_estimate = ulestimate>dlestimate?ulestimate:dlestimate;
295
296
297   /* If we have a total estimate, we can display that and the expected
298          time left */
299   if(total_estimate) {
300     time2str(time_left, total_estimate-(int) data->progress.timespent); 
301     time2str(time_total, total_estimate);
302   }
303   else {
304     /* otherwise we blank those times */
305     strcpy(time_left,  "--:--:--");
306     strcpy(time_total, "--:--:--");
307   }
308   /* The time spent so far is always known */
309   time2str(time_current, data->progress.timespent);
310
311   /* Get the total amount of data expected to get transfered */
312   total_expected_transfer = 
313     (data->progress.flags & PGRS_UL_SIZE_KNOWN?
314      data->progress.size_ul:data->progress.uploaded)+
315     (data->progress.flags & PGRS_DL_SIZE_KNOWN?
316      data->progress.size_dl:data->progress.downloaded);
317       
318   /* We have transfered this much so far */
319   total_transfer = data->progress.downloaded + data->progress.uploaded;
320
321   /* Get the percentage of data transfered so far */
322   if(total_expected_transfer)
323     total_percen=(double)(total_transfer/total_expected_transfer)*100;
324
325   fprintf(data->err,
326           "\r%3d %s  %3d %s  %3d %s  %s  %s %s %s %s %s",
327           (int)total_percen,                            /* total % */
328           max5data(total_expected_transfer, max5[2]),   /* total size */
329           (int)dlpercen,                                /* rcvd % */
330           max5data(data->progress.downloaded, max5[0]), /* rcvd size */
331           (int)ulpercen,                                /* xfer % */
332           max5data(data->progress.uploaded, max5[1]),   /* xfer size */
333
334           max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */
335           max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */
336           time_total,                           /* total time */
337           time_current,                         /* current time */
338           time_left,                            /* time left */
339           max5data(data->progress.current_speed, max5[5]) /* current speed */
340           );
341
342   return 0;
343 }