1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2004, 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(curl_off_t bytes, char *max5)
58 #define ONE_KILOBYTE 1024
59 #define ONE_MEGABYTE (1024*1024)
60 #define ONE_GIGABYTE (1024*1024*1024)
63 sprintf(max5, "%5" FORMAT_OFF_T, bytes);
65 else if(bytes < (10000*ONE_KILOBYTE)) {
66 sprintf(max5, "%4" FORMAT_OFF_T "k", (curl_off_t)(bytes/ONE_KILOBYTE));
68 else if(bytes < (100*ONE_MEGABYTE)) {
69 /* 'XX.XM' is good as long as we're less than 100 megs */
70 sprintf(max5, "%2d.%0dM",
71 (int)(bytes/ONE_MEGABYTE),
72 (int)(bytes%ONE_MEGABYTE)/(ONE_MEGABYTE/10) );
74 #if SIZEOF_CURL_OFF_T > 4
75 else if(bytes < ((curl_off_t)10000*ONE_MEGABYTE)) {
76 sprintf(max5, "%4" FORMAT_OFF_T "M", (curl_off_t)(bytes/ONE_MEGABYTE));
79 /* 10000 MB - 8589934587 GB !! */
80 sprintf(max5, "%2d.%0dG",
81 (int)(bytes/ONE_GIGABYTE),
82 (int)(bytes%ONE_GIGABYTE)/(ONE_GIGABYTE/10) );
85 sprintf(max5, "%4" FORMAT_OFF_T "M", (curl_off_t)(bytes/ONE_MEGABYTE));
93 New proposed interface, 9th of February 2000:
95 pgrsStartNow() - sets start time
96 pgrsSetDownloadSize(x) - known expected download size
97 pgrsSetUploadSize(x) - known expected upload size
98 pgrsSetDownloadCounter() - amount of data currently downloaded
99 pgrsSetUploadCounter() - amount of data currently uploaded
100 pgrsUpdate() - show progress
101 pgrsDone() - transfer complete
105 void Curl_pgrsDone(struct connectdata *conn)
107 struct SessionHandle *data = conn->data;
108 if(!(data->progress.flags & PGRS_HIDE)) {
109 data->progress.lastshow=0;
110 Curl_pgrsUpdate(conn); /* the final (forced) update */
111 if(!data->progress.callback)
112 /* only output if we don't use progress callback */
113 fprintf(data->set.err, "\n");
117 /* reset all times except redirect */
118 void Curl_pgrsResetTimes(struct SessionHandle *data)
120 data->progress.t_nslookup = 0.0;
121 data->progress.t_connect = 0.0;
122 data->progress.t_pretransfer = 0.0;
123 data->progress.t_starttransfer = 0.0;
126 void Curl_pgrsTime(struct SessionHandle *data, timerid timer)
133 case TIMER_STARTSINGLE:
134 /* This is set at the start of a single fetch */
135 data->progress.t_startsingle = Curl_tvnow();
138 case TIMER_NAMELOOKUP:
139 data->progress.t_nslookup =
140 (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0;
143 data->progress.t_connect =
144 (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0;
146 case TIMER_PRETRANSFER:
147 data->progress.t_pretransfer =
148 (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0;
150 case TIMER_STARTTRANSFER:
151 data->progress.t_starttransfer =
152 (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0;
154 case TIMER_POSTRANSFER:
155 /* this is the normal end-of-transfer thing */
158 data->progress.t_redirect =
159 (double)Curl_tvdiff(Curl_tvnow(), data->progress.start)/1000.0;
164 void Curl_pgrsStartNow(struct SessionHandle *data)
166 data->progress.speeder_c = 0; /* reset the progress meter display */
167 data->progress.start = Curl_tvnow();
170 void Curl_pgrsSetDownloadCounter(struct SessionHandle *data, curl_off_t size)
172 data->progress.downloaded = size;
175 void Curl_pgrsSetUploadCounter(struct SessionHandle *data, curl_off_t size)
177 data->progress.uploaded = size;
180 void Curl_pgrsSetDownloadSize(struct SessionHandle *data, curl_off_t size)
182 data->progress.size_dl = size;
184 data->progress.flags |= PGRS_DL_SIZE_KNOWN;
186 data->progress.flags &= ~PGRS_DL_SIZE_KNOWN;
189 void Curl_pgrsSetUploadSize(struct SessionHandle *data, curl_off_t size)
191 data->progress.size_ul = size;
193 data->progress.flags |= PGRS_UL_SIZE_KNOWN;
195 data->progress.flags &= ~PGRS_UL_SIZE_KNOWN;
198 /* EXAMPLE OUTPUT to follow:
200 % Total % Received % Xferd Average Speed Time Curr.
201 Dload Upload Total Current Left Speed
202 100 12345 100 12345 100 12345 12345 12345 12:12:12 12:12:12 12:12:12 12345
206 int Curl_pgrsUpdate(struct connectdata *conn)
214 double total_percen=0;
216 curl_off_t total_transfer;
217 curl_off_t total_expected_transfer;
220 struct SessionHandle *data = conn->data;
222 int nowindex = data->progress.speeder_c% CURR_TIME;
225 int countindex; /* amount of seconds stored in the speeder array */
229 char time_current[10];
234 double total_estimate;
237 if(data->progress.flags & PGRS_HIDE)
238 ; /* We do enter this function even if we don't wanna see anything, since
239 this is were lots of the calculations are being made that will be used
240 even when not displayed! */
241 else if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
242 if (!data->progress.callback) {
243 if(conn->resume_from)
244 fprintf(data->set.err,
245 "** Resuming transfer from byte position %" FORMAT_OFF_T
248 fprintf(data->set.err,
249 " %% Total %% Received %% Xferd Average Speed Time Curr.\n"
250 " Dload Upload Total Current Left Speed\n");
252 data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */
255 now = Curl_tvnow(); /* what time is it */
257 /* The exact time spent so far (from the start) */
258 timespent = (double)Curl_tvdiff (now, data->progress.start)/1000;
260 data->progress.timespent = timespent;
262 /* The average download speed this far */
263 data->progress.dlspeed =
264 data->progress.downloaded/(timespent>0.01?timespent:1);
266 /* The average upload speed this far */
267 data->progress.ulspeed =
268 data->progress.uploaded/(timespent>0.01?timespent:1);
270 if(data->progress.lastshow == Curl_tvlong(now))
271 return 0; /* never update this more than once a second if the end isn't
273 data->progress.lastshow = now.tv_sec;
275 /* Let's do the "current speed" thing, which should use the fastest
276 of the dl/ul speeds. Store the fasted speed at entry 'nowindex'. */
277 data->progress.speeder[ nowindex ] =
278 data->progress.downloaded>data->progress.uploaded?
279 data->progress.downloaded:data->progress.uploaded;
281 /* remember the exact time for this moment */
282 data->progress.speeder_time [ nowindex ] = now;
284 /* advance our speeder_c counter, which is increased every time we get
285 here and we expect it to never wrap as 2^32 is a lot of seconds! */
286 data->progress.speeder_c++;
288 /* figure out how many index entries of data we have stored in our speeder
289 array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of
290 transfer. Imagine, after one second we have filled in two entries,
291 after two seconds we've filled in three entries etc. */
292 countindex = ((data->progress.speeder_c>=CURR_TIME)?
293 CURR_TIME:data->progress.speeder_c) - 1;
295 /* first of all, we don't do this if there's no counted seconds yet */
299 /* Get the index position to compare with the 'nowindex' position.
300 Get the oldest entry possible. While we have less than CURR_TIME
301 entries, the first entry will remain the oldest. */
302 checkindex = (data->progress.speeder_c>=CURR_TIME)?
303 data->progress.speeder_c%CURR_TIME:0;
305 /* Figure out the exact time for the time span */
306 span_ms = Curl_tvdiff(now,
307 data->progress.speeder_time[checkindex]);
309 span_ms=1; /* at least one millisecond MUST have passed */
311 /* Calculate the average speed the last 'countindex' seconds */
312 data->progress.current_speed =
313 (data->progress.speeder[nowindex]-
314 data->progress.speeder[checkindex])/((double)span_ms/1000);
317 /* the first second we use the main average */
318 data->progress.current_speed =
319 (data->progress.ulspeed>data->progress.dlspeed)?
320 data->progress.ulspeed:data->progress.dlspeed;
322 if(data->progress.flags & PGRS_HIDE)
325 else if(data->set.fprogress) {
326 /* There's a callback set, so we call that instead of writing
327 anything ourselves. This really is the way to go. */
328 result= data->set.fprogress(data->set.progress_client,
329 (double)data->progress.size_dl,
330 (double)data->progress.downloaded,
331 (double)data->progress.size_ul,
332 (double)data->progress.uploaded);
334 failf(data, "Callback aborted");
338 /* Figure out the estimated time of arrival for the upload */
339 if((data->progress.flags & PGRS_UL_SIZE_KNOWN) &&
340 (data->progress.ulspeed > 0)) {
341 ulestimate = (double)data->progress.size_ul / data->progress.ulspeed;
342 ulpercen = ((double)data->progress.uploaded / data->progress.size_ul)*100;
345 /* ... and the download */
346 if((data->progress.flags & PGRS_DL_SIZE_KNOWN) &&
347 (data->progress.dlspeed > 0)) {
348 dlestimate = (double)data->progress.size_dl / data->progress.dlspeed;
349 dlpercen = ((double)data->progress.downloaded / data->progress.size_dl)*100;
352 /* Now figure out which of them that is slower and use for the for
354 total_estimate = ulestimate>dlestimate?ulestimate:dlestimate;
357 /* If we have a total estimate, we can display that and the expected
359 if(total_estimate > 0) {
360 time2str(time_left, (int)(total_estimate - data->progress.timespent));
361 time2str(time_total, (int)total_estimate);
364 /* otherwise we blank those times */
365 strcpy(time_left, "--:--:--");
366 strcpy(time_total, "--:--:--");
368 /* The time spent so far is always known */
369 time2str(time_current, (int)data->progress.timespent);
371 /* Get the total amount of data expected to get transfered */
372 total_expected_transfer =
373 (data->progress.flags & PGRS_UL_SIZE_KNOWN?
374 data->progress.size_ul:data->progress.uploaded)+
375 (data->progress.flags & PGRS_DL_SIZE_KNOWN?
376 data->progress.size_dl:data->progress.downloaded);
378 /* We have transfered this much so far */
379 total_transfer = data->progress.downloaded + data->progress.uploaded;
381 /* Get the percentage of data transfered so far */
382 if(total_expected_transfer > 0)
383 total_percen=((double)total_transfer/total_expected_transfer)*100;
385 fprintf(data->set.err,
386 "\r%3d %s %3d %s %3d %s %s %s %s %s %s %s",
387 (int)total_percen, /* total % */
388 max5data(total_expected_transfer, max5[2]), /* total size */
389 (int)dlpercen, /* rcvd % */
390 max5data(data->progress.downloaded, max5[0]), /* rcvd size */
391 (int)ulpercen, /* xfer % */
392 max5data(data->progress.uploaded, max5[1]), /* xfer size */
394 max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */
395 max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */
396 time_total, /* total time */
397 time_current, /* current time */
398 time_left, /* time left */
399 max5data(data->progress.current_speed, max5[5]) /* current speed */
402 /* we flush the output stream to make it appear as soon as possible */
403 fflush(data->set.err);