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.
52 #include <curl/curl.h>
54 enum fcurl_type_e { CFTYPE_NONE=0, CFTYPE_FILE=1, CFTYPE_CURL=2 };
58 enum fcurl_type_e type; /* type of handle */
62 } handle; /* handle */
64 char *buffer; /* buffer to store cached data*/
65 int buffer_len; /* currently allocated buffers length */
66 int buffer_pos; /* end of data in buffer*/
67 int still_running; /* Is background url fetch still in progress */
70 typedef struct fcurl_data URL_FILE;
72 /* exported functions */
73 URL_FILE *url_fopen(char *url,const char *operation);
74 int url_fclose(URL_FILE *file);
75 int url_feof(URL_FILE *file);
76 size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file);
77 char * url_fgets(char *ptr, int size, URL_FILE *file);
78 void url_rewind(URL_FILE *file);
80 /* we use a global one for convenience */
83 /* curl calls this routine to get more data */
85 write_callback(char *buffer,
93 URL_FILE *url = (URL_FILE *)userp;
96 rembuff=url->buffer_len - url->buffer_pos;//remaining space in buffer
100 //not enuf space in buffer
101 newbuff=realloc(url->buffer,url->buffer_len + (size - rembuff));
104 fprintf(stderr,"callback buffer grow failed\n");
109 /* realloc suceeded increase buffer size*/
110 url->buffer_len+=size - rembuff;
113 /*printf("Callback buffer grown to %d bytes\n",url->buffer_len);*/
117 memcpy(&url->buffer[url->buffer_pos], buffer, size);
118 url->buffer_pos += size;
120 /*fprintf(stderr, "callback %d size bytes\n", size);*/
125 /* use to attempt to fill the read buffer up to requested number of bytes */
127 curl_fill_buffer(URL_FILE *file,int want,int waittime)
133 struct timeval timeout;
136 /* only attempt to fill buffer if transactions still running and buffer
137 * doesnt exceed required size already
139 if((!file->still_running) || (file->buffer_pos > want))
142 /* attempt to fill buffer */
149 /* set a suitable timeout to fail on */
150 timeout.tv_sec = 60; /* 1 minute */
153 /* get file descriptors from the transfers */
154 curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
156 rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
167 /* timeout or readable/writable sockets */
168 /* note we *could* be more efficient and not wait for
169 * CURLM_CALL_MULTI_PERFORM to clear here and check it on re-entry
170 * but that gets messy */
171 while(curl_multi_perform(multi_handle, &file->still_running) ==
172 CURLM_CALL_MULTI_PERFORM);
176 } while(file->still_running && (file->buffer_pos < want));
180 /* use to remove want bytes from the front of a files buffer */
182 curl_use_buffer(URL_FILE *file,int want)
184 /* sort out buffer */
185 if((file->buffer_pos - want) <=0)
187 /* ditch buffer - write will recreate */
197 /* move rest down make it available for later */
198 memmove(file->buffer,
200 (file->buffer_pos - want));
202 file->buffer_pos -= want;
210 url_fopen(char *url,const char *operation)
212 /* this code could check for URLs or types in the 'url' and
213 basicly use the real fopen() for standard files */
218 file = (URL_FILE *)malloc(sizeof(URL_FILE));
222 memset(file, 0, sizeof(URL_FILE));
224 if((file->handle.file=fopen(url,operation)))
226 file->type = CFTYPE_FILE; /* marked as URL */
230 file->type = CFTYPE_CURL; /* marked as URL */
231 file->handle.curl = curl_easy_init();
233 curl_easy_setopt(file->handle.curl, CURLOPT_URL, url);
234 curl_easy_setopt(file->handle.curl, CURLOPT_WRITEDATA, file);
235 curl_easy_setopt(file->handle.curl, CURLOPT_VERBOSE, FALSE);
236 curl_easy_setopt(file->handle.curl, CURLOPT_WRITEFUNCTION, write_callback);
239 multi_handle = curl_multi_init();
241 curl_multi_add_handle(multi_handle, file->handle.curl);
243 /* lets start the fetch */
244 while(curl_multi_perform(multi_handle, &file->still_running) ==
245 CURLM_CALL_MULTI_PERFORM );
247 if((file->buffer_pos == 0) && (!file->still_running))
249 /* if still_running is 0 now, we should return NULL */
251 /* make sure the easy handle is not in the multi handle anymore */
252 curl_multi_remove_handle(multi_handle, file->handle.curl);
255 curl_easy_cleanup(file->handle.curl);
266 url_fclose(URL_FILE *file)
268 int ret=0;/* default is good return */
273 ret=fclose(file->handle.file); /* passthrough */
277 /* make sure the easy handle is not in the multi handle anymore */
278 curl_multi_remove_handle(multi_handle, file->handle.curl);
281 curl_easy_cleanup(file->handle.curl);
284 default: /* unknown or supported type - oh dear */
292 free(file->buffer);/* free any allocated buffer space */
300 url_feof(URL_FILE *file)
307 ret=feof(file->handle.file);
311 if((file->buffer_pos == 0) && (!file->still_running))
314 default: /* unknown or supported type - oh dear */
323 url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file)
330 want=fread(ptr,size,nmemb,file->handle.file);
336 curl_fill_buffer(file,want,1);
338 /* check if theres data in the buffer - if not curl_fill_buffer()
339 * either errored or EOF */
340 if(!file->buffer_pos)
343 /* ensure only available data is considered */
344 if(file->buffer_pos < want)
345 want = file->buffer_pos;
347 /* xfer data to caller */
348 memcpy(ptr, file->buffer, want);
350 curl_use_buffer(file,want);
352 want = want / size; /* number of items - nb correct op - checked
355 /*printf("(fread) return %d bytes %d left\n", want,file->buffer_pos);*/
358 default: /* unknown or supported type - oh dear */
368 url_fgets(char *ptr, int size, URL_FILE *file)
370 int want = size - 1;/* always need to leave room for zero termination */
376 ptr = fgets(ptr,size,file->handle.file);
380 curl_fill_buffer(file,want,1);
382 /* check if theres data in the buffer - if not fill either errored or
384 if(!file->buffer_pos)
387 /* ensure only available data is considered */
388 if(file->buffer_pos < want)
389 want = file->buffer_pos;
391 /*buffer contains data */
392 /* look for newline or eof */
393 for(loop=0;loop < want;loop++)
395 if(file->buffer[loop] == '\n')
397 want=loop+1;/* include newline */
402 /* xfer data to caller */
403 memcpy(ptr, file->buffer, want);
404 ptr[want]=0;/* allways null terminate */
406 curl_use_buffer(file,want);
408 /*printf("(fgets) return %d bytes %d left\n", want,file->buffer_pos);*/
411 default: /* unknown or supported type - oh dear */
417 return ptr;/*success */
421 url_rewind(URL_FILE *file)
426 rewind(file->handle.file); /* passthrough */
430 /* halt transaction */
431 curl_multi_remove_handle(multi_handle, file->handle.curl);
434 curl_multi_add_handle(multi_handle, file->handle.curl);
436 /* ditch buffer - write will recreate - resets stream pos*/
446 default: /* unknown or supported type - oh dear */
454 /* Small main program to retrive from a url using fgets and fread saving the
455 * output to two test files (note the fgets method will corrupt binary files if
456 * they contain 0 chars */
458 main(int argc, char *argv[])
469 url="http://192.168.7.3/testfile";/* default to testurl */
473 url=argv[1];/* use passed url */
476 /* copy from url line by line with fgets */
477 outf=fopen("fgets.test","w+");
480 perror("couldnt open fgets output file\n");
484 handle = url_fopen(url, "r");
487 printf("couldn't url_fopen()\n");
492 while(!url_feof(handle))
494 url_fgets(buffer,sizeof(buffer),handle);
495 fwrite(buffer,1,strlen(buffer),outf);
503 /* Copy from url with fread */
504 outf=fopen("fread.test","w+");
507 perror("couldnt open fread output file\n");
511 handle = url_fopen("testfile", "r");
513 printf("couldn't url_fopen()\n");
519 nread = url_fread(buffer, 1,sizeof(buffer), handle);
520 fwrite(buffer,1,nread,outf);
529 outf=fopen("rewind.test","w+");
532 perror("couldnt open fread output file\n");
536 handle = url_fopen("testfile", "r");
538 printf("couldn't url_fopen()\n");
543 nread = url_fread(buffer, 1,sizeof(buffer), handle);
544 fwrite(buffer,1,nread,outf);
548 fwrite(buffer,1,1,outf);
550 nread = url_fread(buffer, 1,sizeof(buffer), handle);
551 fwrite(buffer,1,nread,outf);
559 return 0;/* all done */