1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2003, Daniel Stenberg, <daniel@haxx.se>, et al.
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 http://curl.haxx.se/docs/copyright.html.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
22 ***************************************************************************/
30 * later we use _scrsize to determine the screen width, this emx library
31 * function needs stdlib.h to be included */
36 #include <curl/curl.h>
42 #define _MPRINTF_REPLACE /* use our functions only */
43 #include <curl/mprintf.h>
46 static void time2str(char *r, int t)
49 int m = (t-(h*3600))/60;
50 int s = (t-(h*3600)-(m*60));
51 sprintf(r,"%2d:%02d:%02d",h,m,s);
54 /* The point of this function would be to return a string of the input data,
55 but never longer than 5 columns. Add suffix k, M, G when suitable... */
56 static char *max5data(double bytes, char *max5)
58 #define ONE_KILOBYTE 1024
59 #define ONE_MEGABYTE (1024*1024)
62 sprintf(max5, "%5d", (int)bytes);
65 if(bytes < (9999*ONE_KILOBYTE)) {
66 sprintf(max5, "%4dk", (int)bytes/ONE_KILOBYTE);
69 if(bytes < (100*ONE_MEGABYTE)) {
70 /* 'XX.XM' is good as long as we're less than 100 megs */
71 sprintf(max5, "%4.1fM", bytes/ONE_MEGABYTE);
74 sprintf(max5, "%4dM", (int)bytes/ONE_MEGABYTE);
80 New proposed interface, 9th of February 2000:
82 pgrsStartNow() - sets start time
83 pgrsSetDownloadSize(x) - known expected download size
84 pgrsSetUploadSize(x) - known expected upload size
85 pgrsSetDownloadCounter() - amount of data currently downloaded
86 pgrsSetUploadCounter() - amount of data currently uploaded
87 pgrsUpdate() - show progress
88 pgrsDone() - transfer complete
92 void Curl_pgrsDone(struct connectdata *conn)
94 struct SessionHandle *data = conn->data;
95 if(!(data->progress.flags & PGRS_HIDE)) {
96 data->progress.lastshow=0;
97 Curl_pgrsUpdate(conn); /* the final (forced) update */
98 if(!data->progress.callback)
99 /* only output if we don't use progress callback */
100 fprintf(data->set.err, "\n");
104 /* reset all times except redirect */
105 void Curl_pgrsResetTimes(struct SessionHandle *data)
107 data->progress.t_nslookup = 0.0;
108 data->progress.t_connect = 0.0;
109 data->progress.t_pretransfer = 0.0;
110 data->progress.t_starttransfer = 0.0;
113 void Curl_pgrsTime(struct SessionHandle *data, timerid timer)
120 case TIMER_STARTSINGLE:
121 /* This is set at the start of a single fetch */
122 data->progress.t_startsingle = Curl_tvnow();
125 case TIMER_NAMELOOKUP:
126 data->progress.t_nslookup =
127 (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0;
130 data->progress.t_connect =
131 (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0;
133 case TIMER_PRETRANSFER:
134 data->progress.t_pretransfer =
135 (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0;
137 case TIMER_STARTTRANSFER:
138 data->progress.t_starttransfer =
139 (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0;
141 case TIMER_POSTRANSFER:
142 /* this is the normal end-of-transfer thing */
145 data->progress.t_redirect =
146 (double)Curl_tvdiff(Curl_tvnow(), data->progress.start)/1000.0;
151 void Curl_pgrsStartNow(struct SessionHandle *data)
153 data->progress.speeder_c = 0; /* reset the progress meter display */
154 data->progress.start = Curl_tvnow();
157 void Curl_pgrsSetDownloadCounter(struct SessionHandle *data, double size)
159 data->progress.downloaded = size;
162 void Curl_pgrsSetUploadCounter(struct SessionHandle *data, double size)
164 data->progress.uploaded = size;
167 void Curl_pgrsSetDownloadSize(struct SessionHandle *data, double size)
169 data->progress.size_dl = size;
171 data->progress.flags |= PGRS_DL_SIZE_KNOWN;
173 data->progress.flags &= ~PGRS_DL_SIZE_KNOWN;
176 void Curl_pgrsSetUploadSize(struct SessionHandle *data, double size)
178 data->progress.size_ul = size;
180 data->progress.flags |= PGRS_UL_SIZE_KNOWN;
182 data->progress.flags &= ~PGRS_UL_SIZE_KNOWN;
185 /* EXAMPLE OUTPUT to follow:
187 % Total % Received % Xferd Average Speed Time Curr.
188 Dload Upload Total Current Left Speed
189 100 12345 100 12345 100 12345 12345 12345 12:12:12 12:12:12 12:12:12 12345
193 int Curl_pgrsUpdate(struct connectdata *conn)
201 double total_percen=0;
203 double total_transfer;
204 double total_expected_transfer;
207 struct SessionHandle *data = conn->data;
209 int nowindex = data->progress.speeder_c% CURR_TIME;
212 int countindex; /* amount of seconds stored in the speeder array */
216 char time_current[10];
221 double total_estimate;
224 if(data->progress.flags & PGRS_HIDE)
225 ; /* We do enter this function even if we don't wanna see anything, since
226 this is were lots of the calculations are being made that will be used
227 even when not displayed! */
228 else if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
229 if (!data->progress.callback) {
230 if(conn->resume_from)
231 fprintf(data->set.err,
232 "** Resuming transfer from byte position %Od\n",
234 fprintf(data->set.err,
235 " %% Total %% Received %% Xferd Average Speed Time Curr.\n"
236 " Dload Upload Total Current Left Speed\n");
238 data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */
241 now = Curl_tvnow(); /* what time is it */
243 /* The exact time spent so far (from the start) */
244 timespent = (double)Curl_tvdiff (now, data->progress.start)/1000;
246 data->progress.timespent = timespent;
248 /* The average download speed this far */
249 data->progress.dlspeed =
250 data->progress.downloaded/(timespent>0.01?timespent:1);
252 /* The average upload speed this far */
253 data->progress.ulspeed =
254 data->progress.uploaded/(timespent>0.01?timespent:1);
256 if(data->progress.lastshow == Curl_tvlong(now))
257 return 0; /* never update this more than once a second if the end isn't
259 data->progress.lastshow = now.tv_sec;
261 /* Let's do the "current speed" thing, which should use the fastest
262 of the dl/ul speeds. Store the fasted speed at entry 'nowindex'. */
263 data->progress.speeder[ nowindex ] =
264 data->progress.downloaded>data->progress.uploaded?
265 data->progress.downloaded:data->progress.uploaded;
267 /* remember the exact time for this moment */
268 data->progress.speeder_time [ nowindex ] = now;
270 /* advance our speeder_c counter, which is increased every time we get
271 here and we expect it to never wrap as 2^32 is a lot of seconds! */
272 data->progress.speeder_c++;
274 /* figure out how many index entries of data we have stored in our speeder
275 array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of
276 transfer. Imagine, after one second we have filled in two entries,
277 after two seconds we've filled in three entries etc. */
278 countindex = ((data->progress.speeder_c>=CURR_TIME)?
279 CURR_TIME:data->progress.speeder_c) - 1;
281 /* first of all, we don't do this if there's no counted seconds yet */
285 /* Get the index position to compare with the 'nowindex' position.
286 Get the oldest entry possible. While we have less than CURR_TIME
287 entries, the first entry will remain the oldest. */
288 checkindex = (data->progress.speeder_c>=CURR_TIME)?
289 data->progress.speeder_c%CURR_TIME:0;
291 /* Figure out the exact time for the time span */
292 span_ms = Curl_tvdiff(now,
293 data->progress.speeder_time[checkindex]);
295 span_ms=1; /* at least one millisecond MUST have passed */
297 /* Calculate the average speed the last 'countindex' seconds */
298 data->progress.current_speed =
299 (data->progress.speeder[nowindex]-
300 data->progress.speeder[checkindex])/((double)span_ms/1000);
303 /* the first second we use the main average */
304 data->progress.current_speed =
305 (data->progress.ulspeed>data->progress.dlspeed)?
306 data->progress.ulspeed:data->progress.dlspeed;
308 if(data->progress.flags & PGRS_HIDE)
311 else if(data->set.fprogress) {
312 /* There's a callback set, so we call that instead of writing
313 anything ourselves. This really is the way to go. */
314 result= data->set.fprogress(data->set.progress_client,
315 data->progress.size_dl,
316 data->progress.downloaded,
317 data->progress.size_ul,
318 data->progress.uploaded);
320 failf(data, "Callback aborted");
324 /* Figure out the estimated time of arrival for the upload */
325 if((data->progress.flags & PGRS_UL_SIZE_KNOWN) && data->progress.ulspeed){
326 ulestimate = data->progress.size_ul / data->progress.ulspeed;
327 ulpercen = (data->progress.uploaded / data->progress.size_ul)*100;
330 /* ... and the download */
331 if((data->progress.flags & PGRS_DL_SIZE_KNOWN) && data->progress.dlspeed) {
332 dlestimate = data->progress.size_dl / data->progress.dlspeed;
333 dlpercen = (data->progress.downloaded / data->progress.size_dl)*100;
336 /* Now figure out which of them that is slower and use for the for
338 total_estimate = ulestimate>dlestimate?ulestimate:dlestimate;
341 /* If we have a total estimate, we can display that and the expected
344 time2str(time_left, (int)(total_estimate - data->progress.timespent));
345 time2str(time_total, (int)total_estimate);
348 /* otherwise we blank those times */
349 strcpy(time_left, "--:--:--");
350 strcpy(time_total, "--:--:--");
352 /* The time spent so far is always known */
353 time2str(time_current, (int)data->progress.timespent);
355 /* Get the total amount of data expected to get transfered */
356 total_expected_transfer =
357 (data->progress.flags & PGRS_UL_SIZE_KNOWN?
358 data->progress.size_ul:data->progress.uploaded)+
359 (data->progress.flags & PGRS_DL_SIZE_KNOWN?
360 data->progress.size_dl:data->progress.downloaded);
362 /* We have transfered this much so far */
363 total_transfer = data->progress.downloaded + data->progress.uploaded;
365 /* Get the percentage of data transfered so far */
366 if(total_expected_transfer)
367 total_percen=(double)(total_transfer/total_expected_transfer)*100;
369 fprintf(data->set.err,
370 "\r%3d %s %3d %s %3d %s %s %s %s %s %s %s",
371 (int)total_percen, /* total % */
372 max5data(total_expected_transfer, max5[2]), /* total size */
373 (int)dlpercen, /* rcvd % */
374 max5data(data->progress.downloaded, max5[0]), /* rcvd size */
375 (int)ulpercen, /* xfer % */
376 max5data(data->progress.uploaded, max5[1]), /* xfer size */
378 max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */
379 max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */
380 time_total, /* total time */
381 time_current, /* current time */
382 time_left, /* time left */
383 max5data(data->progress.current_speed, max5[5]) /* current speed */
386 /* we flush the output stream to make it appear as soon as possible */
387 fflush(data->set.err);