1 /*****************************************************************************
3 * This example source code introduces a c library buffered I/O interface to
4 * URL reads it supports fopen(), fread(), fgets(), feof(), fclose(),
5 * rewind(). Supported functions have identical prototypes to their normal c
6 * lib namesakes and are preceaded by url_ .
8 * Using this code you can replace your program's fopen() with url_fopen()
9 * and fread() with url_fread() and it become possible to read remote streams
10 * instead of (only) local files. Local files (ie those that can be directly
11 * fopened) will drop back to using the underlying clib implementations
13 * See the main() function at the bottom that shows an app that retrives from a
14 * specified url using fgets() and fread() and saves as two output files.
16 * Coyright (c)2003 Simtec Electronics
18 * Re-implemented by Vincent Sanders <vince@kyllikki.org> with extensive
19 * reference to original curl example code
21 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted provided that the following conditions
24 * 1. Redistributions of source code must retain the above copyright
25 * notice, this list of conditions and the following disclaimer.
26 * 2. Redistributions in binary form must reproduce the above copyright
27 * notice, this list of conditions and the following disclaimer in the
28 * documentation and/or other materials provided with the distribution.
29 * 3. The name of the author may not be used to endorse or promote products
30 * derived from this software without specific prior written permission.
32 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
33 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
35 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
36 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
37 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
41 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 * This example requires libcurl 7.9.7 or later.
49 # include <sys/time.h>
54 #include <curl/curl.h>
56 enum fcurl_type_e
{ CFTYPE_NONE
=0, CFTYPE_FILE
=1, CFTYPE_CURL
=2 };
60 enum fcurl_type_e type
; /* type of handle */
64 } handle
; /* handle */
66 char *buffer
; /* buffer to store cached data*/
67 int buffer_len
; /* currently allocated buffers length */
68 int buffer_pos
; /* end of data in buffer*/
69 int still_running
; /* Is background url fetch still in progress */
72 typedef struct fcurl_data URL_FILE
;
74 /* exported functions */
75 URL_FILE
*url_fopen(const char *url
,const char *operation
);
76 int url_fclose(URL_FILE
*file
);
77 int url_feof(URL_FILE
*file
);
78 size_t url_fread(void *ptr
, size_t size
, size_t nmemb
, URL_FILE
*file
);
79 char * url_fgets(char *ptr
, int size
, URL_FILE
*file
);
80 void url_rewind(URL_FILE
*file
);
82 /* we use a global one for convenience */
85 /* curl calls this routine to get more data */
87 write_callback(char *buffer
,
95 URL_FILE
*url
= (URL_FILE
*)userp
;
98 rembuff
=url
->buffer_len
- url
->buffer_pos
; /* remaining space in buffer */
102 /* not enough space in buffer */
103 newbuff
=realloc(url
->buffer
,url
->buffer_len
+ (size
- rembuff
));
106 fprintf(stderr
,"callback buffer grow failed\n");
111 /* realloc suceeded increase buffer size*/
112 url
->buffer_len
+=size
- rembuff
;
115 /*printf("Callback buffer grown to %d bytes\n",url->buffer_len);*/
119 memcpy(&url
->buffer
[url
->buffer_pos
], buffer
, size
);
120 url
->buffer_pos
+= size
;
122 /*fprintf(stderr, "callback %d size bytes\n", size);*/
127 /* use to attempt to fill the read buffer up to requested number of bytes */
129 fill_buffer(URL_FILE
*file
,int want
,int waittime
)
135 struct timeval timeout
;
138 /* only attempt to fill buffer if transactions still running and buffer
139 * doesnt exceed required size already
141 if((!file
->still_running
) || (file
->buffer_pos
> want
))
144 /* attempt to fill buffer */
151 /* set a suitable timeout to fail on */
152 timeout
.tv_sec
= 60; /* 1 minute */
155 /* get file descriptors from the transfers */
156 curl_multi_fdset(multi_handle
, &fdread
, &fdwrite
, &fdexcep
, &maxfd
);
158 /* In a real-world program you OF COURSE check the return code of the
159 function calls, *and* you make sure that maxfd is bigger than -1
160 so that the call to select() below makes sense! */
162 rc
= select(maxfd
+1, &fdread
, &fdwrite
, &fdexcep
, &timeout
);
173 /* timeout or readable/writable sockets */
174 /* note we *could* be more efficient and not wait for
175 * CURLM_CALL_MULTI_PERFORM to clear here and check it on re-entry
176 * but that gets messy */
177 while(curl_multi_perform(multi_handle
, &file
->still_running
) ==
178 CURLM_CALL_MULTI_PERFORM
);
182 } while(file
->still_running
&& (file
->buffer_pos
< want
));
186 /* use to remove want bytes from the front of a files buffer */
188 use_buffer(URL_FILE
*file
,int want
)
190 /* sort out buffer */
191 if((file
->buffer_pos
- want
) <=0)
193 /* ditch buffer - write will recreate */
203 /* move rest down make it available for later */
204 memmove(file
->buffer
,
206 (file
->buffer_pos
- want
));
208 file
->buffer_pos
-= want
;
216 url_fopen(const char *url
,const char *operation
)
218 /* this code could check for URLs or types in the 'url' and
219 basicly use the real fopen() for standard files */
224 file
= (URL_FILE
*)malloc(sizeof(URL_FILE
));
228 memset(file
, 0, sizeof(URL_FILE
));
230 if((file
->handle
.file
=fopen(url
,operation
)))
232 file
->type
= CFTYPE_FILE
; /* marked as URL */
236 file
->type
= CFTYPE_CURL
; /* marked as URL */
237 file
->handle
.curl
= curl_easy_init();
239 curl_easy_setopt(file
->handle
.curl
, CURLOPT_URL
, url
);
240 curl_easy_setopt(file
->handle
.curl
, CURLOPT_WRITEDATA
, file
);
241 curl_easy_setopt(file
->handle
.curl
, CURLOPT_VERBOSE
, 0L);
242 curl_easy_setopt(file
->handle
.curl
, CURLOPT_WRITEFUNCTION
, write_callback
);
245 multi_handle
= curl_multi_init();
247 curl_multi_add_handle(multi_handle
, file
->handle
.curl
);
249 /* lets start the fetch */
250 while(curl_multi_perform(multi_handle
, &file
->still_running
) ==
251 CURLM_CALL_MULTI_PERFORM
);
253 if((file
->buffer_pos
== 0) && (!file
->still_running
))
255 /* if still_running is 0 now, we should return NULL */
257 /* make sure the easy handle is not in the multi handle anymore */
258 curl_multi_remove_handle(multi_handle
, file
->handle
.curl
);
261 curl_easy_cleanup(file
->handle
.curl
);
272 url_fclose(URL_FILE
*file
)
274 int ret
=0;/* default is good return */
279 ret
=fclose(file
->handle
.file
); /* passthrough */
283 /* make sure the easy handle is not in the multi handle anymore */
284 curl_multi_remove_handle(multi_handle
, file
->handle
.curl
);
287 curl_easy_cleanup(file
->handle
.curl
);
290 default: /* unknown or supported type - oh dear */
298 free(file
->buffer
);/* free any allocated buffer space */
306 url_feof(URL_FILE
*file
)
313 ret
=feof(file
->handle
.file
);
317 if((file
->buffer_pos
== 0) && (!file
->still_running
))
320 default: /* unknown or supported type - oh dear */
329 url_fread(void *ptr
, size_t size
, size_t nmemb
, URL_FILE
*file
)
336 want
=fread(ptr
,size
,nmemb
,file
->handle
.file
);
342 fill_buffer(file
,want
,1);
344 /* check if theres data in the buffer - if not fill_buffer()
345 * either errored or EOF */
346 if(!file
->buffer_pos
)
349 /* ensure only available data is considered */
350 if(file
->buffer_pos
< want
)
351 want
= file
->buffer_pos
;
353 /* xfer data to caller */
354 memcpy(ptr
, file
->buffer
, want
);
356 use_buffer(file
,want
);
358 want
= want
/ size
; /* number of items - nb correct op - checked
361 /*printf("(fread) return %d bytes %d left\n", want,file->buffer_pos);*/
364 default: /* unknown or supported type - oh dear */
374 url_fgets(char *ptr
, int size
, URL_FILE
*file
)
376 int want
= size
- 1;/* always need to leave room for zero termination */
382 ptr
= fgets(ptr
,size
,file
->handle
.file
);
386 fill_buffer(file
,want
,1);
388 /* check if theres data in the buffer - if not fill either errored or
390 if(!file
->buffer_pos
)
393 /* ensure only available data is considered */
394 if(file
->buffer_pos
< want
)
395 want
= file
->buffer_pos
;
397 /*buffer contains data */
398 /* look for newline or eof */
399 for(loop
=0;loop
< want
;loop
++)
401 if(file
->buffer
[loop
] == '\n')
403 want
=loop
+1;/* include newline */
408 /* xfer data to caller */
409 memcpy(ptr
, file
->buffer
, want
);
410 ptr
[want
]=0;/* allways null terminate */
412 use_buffer(file
,want
);
414 /*printf("(fgets) return %d bytes %d left\n", want,file->buffer_pos);*/
417 default: /* unknown or supported type - oh dear */
423 return ptr
;/*success */
427 url_rewind(URL_FILE
*file
)
432 rewind(file
->handle
.file
); /* passthrough */
436 /* halt transaction */
437 curl_multi_remove_handle(multi_handle
, file
->handle
.curl
);
440 curl_multi_add_handle(multi_handle
, file
->handle
.curl
);
442 /* ditch buffer - write will recreate - resets stream pos*/
452 default: /* unknown or supported type - oh dear */
460 /* Small main program to retrive from a url using fgets and fread saving the
461 * output to two test files (note the fgets method will corrupt binary files if
462 * they contain 0 chars */
464 main(int argc
, char *argv
[])
475 url
="http://192.168.7.3/testfile";/* default to testurl */
479 url
=argv
[1];/* use passed url */
482 /* copy from url line by line with fgets */
483 outf
=fopen("fgets.test","w+");
486 perror("couldn't open fgets output file\n");
490 handle
= url_fopen(url
, "r");
493 printf("couldn't url_fopen() %s\n", url
);
498 while(!url_feof(handle
))
500 url_fgets(buffer
,sizeof(buffer
),handle
);
501 fwrite(buffer
,1,strlen(buffer
),outf
);
509 /* Copy from url with fread */
510 outf
=fopen("fread.test","w+");
513 perror("couldn't open fread output file\n");
517 handle
= url_fopen("testfile", "r");
519 printf("couldn't url_fopen() testfile\n");
525 nread
= url_fread(buffer
, 1,sizeof(buffer
), handle
);
526 fwrite(buffer
,1,nread
,outf
);
535 outf
=fopen("rewind.test","w+");
538 perror("couldn't open fread output file\n");
542 handle
= url_fopen("testfile", "r");
544 printf("couldn't url_fopen() testfile\n");
549 nread
= url_fread(buffer
, 1,sizeof(buffer
), handle
);
550 fwrite(buffer
,1,nread
,outf
);
554 fwrite(buffer
,1,1,outf
);
556 nread
= url_fread(buffer
, 1,sizeof(buffer
), handle
);
557 fwrite(buffer
,1,nread
,outf
);
565 return 0;/* all done */