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