Initial commit of visual studio 9 git build superproject.
[git-build-vc9.git] / curl / docs / examples / fopen.c
blob0a9e9e30d599968aac7a592efbf0e1ef5f511c0c
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
23 * are met:
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.
46 #include <stdio.h>
47 #include <string.h>
48 #ifndef WIN32
49 # include <sys/time.h>
50 #endif
51 #include <stdlib.h>
52 #include <errno.h>
54 #include <curl/curl.h>
56 enum fcurl_type_e { CFTYPE_NONE=0, CFTYPE_FILE=1, CFTYPE_CURL=2 };
58 struct fcurl_data
60 enum fcurl_type_e type; /* type of handle */
61 union {
62 CURL *curl;
63 FILE *file;
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 */
83 CURLM *multi_handle;
85 /* curl calls this routine to get more data */
86 static size_t
87 write_callback(char *buffer,
88 size_t size,
89 size_t nitems,
90 void *userp)
92 char *newbuff;
93 int rembuff;
95 URL_FILE *url = (URL_FILE *)userp;
96 size *= nitems;
98 rembuff=url->buffer_len - url->buffer_pos; /* remaining space in buffer */
100 if(size > rembuff)
102 /* not enough space in buffer */
103 newbuff=realloc(url->buffer,url->buffer_len + (size - rembuff));
104 if(newbuff==NULL)
106 fprintf(stderr,"callback buffer grow failed\n");
107 size=rembuff;
109 else
111 /* realloc suceeded increase buffer size*/
112 url->buffer_len+=size - rembuff;
113 url->buffer=newbuff;
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);*/
124 return size;
127 /* use to attempt to fill the read buffer up to requested number of bytes */
128 static int
129 fill_buffer(URL_FILE *file,int want,int waittime)
131 fd_set fdread;
132 fd_set fdwrite;
133 fd_set fdexcep;
134 int maxfd;
135 struct timeval timeout;
136 int rc;
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))
142 return 0;
144 /* attempt to fill buffer */
147 FD_ZERO(&fdread);
148 FD_ZERO(&fdwrite);
149 FD_ZERO(&fdexcep);
151 /* set a suitable timeout to fail on */
152 timeout.tv_sec = 60; /* 1 minute */
153 timeout.tv_usec = 0;
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);
164 switch(rc) {
165 case -1:
166 /* select error */
167 break;
169 case 0:
170 break;
172 default:
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);
180 break;
182 } while(file->still_running && (file->buffer_pos < want));
183 return 1;
186 /* use to remove want bytes from the front of a files buffer */
187 static int
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 */
194 if(file->buffer)
195 free(file->buffer);
197 file->buffer=NULL;
198 file->buffer_pos=0;
199 file->buffer_len=0;
201 else
203 /* move rest down make it available for later */
204 memmove(file->buffer,
205 &file->buffer[want],
206 (file->buffer_pos - want));
208 file->buffer_pos -= want;
210 return 0;
215 URL_FILE *
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 */
221 URL_FILE *file;
222 (void)operation;
224 file = (URL_FILE *)malloc(sizeof(URL_FILE));
225 if(!file)
226 return NULL;
228 memset(file, 0, sizeof(URL_FILE));
230 if((file->handle.file=fopen(url,operation)))
232 file->type = CFTYPE_FILE; /* marked as URL */
234 else
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);
244 if(!multi_handle)
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);
260 /* cleanup */
261 curl_easy_cleanup(file->handle.curl);
263 free(file);
265 file = NULL;
268 return file;
272 url_fclose(URL_FILE *file)
274 int ret=0;/* default is good return */
276 switch(file->type)
278 case CFTYPE_FILE:
279 ret=fclose(file->handle.file); /* passthrough */
280 break;
282 case CFTYPE_CURL:
283 /* make sure the easy handle is not in the multi handle anymore */
284 curl_multi_remove_handle(multi_handle, file->handle.curl);
286 /* cleanup */
287 curl_easy_cleanup(file->handle.curl);
288 break;
290 default: /* unknown or supported type - oh dear */
291 ret=EOF;
292 errno=EBADF;
293 break;
297 if(file->buffer)
298 free(file->buffer);/* free any allocated buffer space */
300 free(file);
302 return ret;
306 url_feof(URL_FILE *file)
308 int ret=0;
310 switch(file->type)
312 case CFTYPE_FILE:
313 ret=feof(file->handle.file);
314 break;
316 case CFTYPE_CURL:
317 if((file->buffer_pos == 0) && (!file->still_running))
318 ret = 1;
319 break;
320 default: /* unknown or supported type - oh dear */
321 ret=-1;
322 errno=EBADF;
323 break;
325 return ret;
328 size_t
329 url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file)
331 size_t want;
333 switch(file->type)
335 case CFTYPE_FILE:
336 want=fread(ptr,size,nmemb,file->handle.file);
337 break;
339 case CFTYPE_CURL:
340 want = nmemb * size;
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)
347 return 0;
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
359 * with glibc code*/
361 /*printf("(fread) return %d bytes %d left\n", want,file->buffer_pos);*/
362 break;
364 default: /* unknown or supported type - oh dear */
365 want=0;
366 errno=EBADF;
367 break;
370 return want;
373 char *
374 url_fgets(char *ptr, int size, URL_FILE *file)
376 int want = size - 1;/* always need to leave room for zero termination */
377 int loop;
379 switch(file->type)
381 case CFTYPE_FILE:
382 ptr = fgets(ptr,size,file->handle.file);
383 break;
385 case CFTYPE_CURL:
386 fill_buffer(file,want,1);
388 /* check if theres data in the buffer - if not fill either errored or
389 * EOF */
390 if(!file->buffer_pos)
391 return NULL;
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 */
404 break;
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);*/
415 break;
417 default: /* unknown or supported type - oh dear */
418 ptr=NULL;
419 errno=EBADF;
420 break;
423 return ptr;/*success */
426 void
427 url_rewind(URL_FILE *file)
429 switch(file->type)
431 case CFTYPE_FILE:
432 rewind(file->handle.file); /* passthrough */
433 break;
435 case CFTYPE_CURL:
436 /* halt transaction */
437 curl_multi_remove_handle(multi_handle, file->handle.curl);
439 /* restart */
440 curl_multi_add_handle(multi_handle, file->handle.curl);
442 /* ditch buffer - write will recreate - resets stream pos*/
443 if(file->buffer)
444 free(file->buffer);
446 file->buffer=NULL;
447 file->buffer_pos=0;
448 file->buffer_len=0;
450 break;
452 default: /* unknown or supported type - oh dear */
453 break;
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[])
466 URL_FILE *handle;
467 FILE *outf;
469 int nread;
470 char buffer[256];
471 const char *url;
473 if(argc < 2)
475 url="http://192.168.7.3/testfile";/* default to testurl */
477 else
479 url=argv[1];/* use passed url */
482 /* copy from url line by line with fgets */
483 outf=fopen("fgets.test","w+");
484 if(!outf)
486 perror("couldn't open fgets output file\n");
487 return 1;
490 handle = url_fopen(url, "r");
491 if(!handle)
493 printf("couldn't url_fopen() %s\n", url);
494 fclose(outf);
495 return 2;
498 while(!url_feof(handle))
500 url_fgets(buffer,sizeof(buffer),handle);
501 fwrite(buffer,1,strlen(buffer),outf);
504 url_fclose(handle);
506 fclose(outf);
509 /* Copy from url with fread */
510 outf=fopen("fread.test","w+");
511 if(!outf)
513 perror("couldn't open fread output file\n");
514 return 1;
517 handle = url_fopen("testfile", "r");
518 if(!handle) {
519 printf("couldn't url_fopen() testfile\n");
520 fclose(outf);
521 return 2;
524 do {
525 nread = url_fread(buffer, 1,sizeof(buffer), handle);
526 fwrite(buffer,1,nread,outf);
527 } while(nread);
529 url_fclose(handle);
531 fclose(outf);
534 /* Test rewind */
535 outf=fopen("rewind.test","w+");
536 if(!outf)
538 perror("couldn't open fread output file\n");
539 return 1;
542 handle = url_fopen("testfile", "r");
543 if(!handle) {
544 printf("couldn't url_fopen() testfile\n");
545 fclose(outf);
546 return 2;
549 nread = url_fread(buffer, 1,sizeof(buffer), handle);
550 fwrite(buffer,1,nread,outf);
551 url_rewind(handle);
553 buffer[0]='\n';
554 fwrite(buffer,1,1,outf);
556 nread = url_fread(buffer, 1,sizeof(buffer), handle);
557 fwrite(buffer,1,nread,outf);
560 url_fclose(handle);
562 fclose(outf);
565 return 0;/* all done */