fopen.c
Go to the documentation of this file.
00001 /*****************************************************************************
00002  *
00003  * This example source code introduces a c library buffered I/O interface to
00004  * URL reads it supports fopen(), fread(), fgets(), feof(), fclose(),
00005  * rewind(). Supported functions have identical prototypes to their normal c
00006  * lib namesakes and are preceaded by url_ .
00007  *
00008  * Using this code you can replace your program's fopen() with url_fopen()
00009  * and fread() with url_fread() and it become possible to read remote streams
00010  * instead of (only) local files. Local files (ie those that can be directly
00011  * fopened) will drop back to using the underlying clib implementations
00012  *
00013  * See the main() function at the bottom that shows an app that retrives from a
00014  * specified url using fgets() and fread() and saves as two output files.
00015  *
00016  * Copyright (c) 2003 Simtec Electronics
00017  *
00018  * Re-implemented by Vincent Sanders <vince@kyllikki.org> with extensive
00019  * reference to original curl example code
00020  *
00021  * Redistribution and use in source and binary forms, with or without
00022  * modification, are permitted provided that the following conditions
00023  * are met:
00024  * 1. Redistributions of source code must retain the above copyright
00025  *    notice, this list of conditions and the following disclaimer.
00026  * 2. Redistributions in binary form must reproduce the above copyright
00027  *    notice, this list of conditions and the following disclaimer in the
00028  *    documentation and/or other materials provided with the distribution.
00029  * 3. The name of the author may not be used to endorse or promote products
00030  *    derived from this software without specific prior written permission.
00031  *
00032  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00033  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00034  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00035  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00036  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00037  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00038  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00039  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00040  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00041  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00042  *
00043  * This example requires libcurl 7.9.7 or later.
00044  */
00045 /* <DESC>
00046  * implements an fopen() abstraction allowing reading from URLs
00047  * </DESC>
00048  */
00049 
00050 #include <stdio.h>
00051 #include <string.h>
00052 #ifndef WIN32
00053 #  include <sys/time.h>
00054 #endif
00055 #include <stdlib.h>
00056 #include <errno.h>
00057 
00058 #include <curl/curl.h>
00059 
00060 enum fcurl_type_e {
00061   CFTYPE_NONE=0,
00062   CFTYPE_FILE=1,
00063   CFTYPE_CURL=2
00064 };
00065 
00066 struct fcurl_data
00067 {
00068   enum fcurl_type_e type;     /* type of handle */
00069   union {
00070     CURL *curl;
00071     FILE *file;
00072   } handle;                   /* handle */
00073 
00074   char *buffer;               /* buffer to store cached data*/
00075   size_t buffer_len;          /* currently allocated buffers length */
00076   size_t buffer_pos;          /* end of data in buffer*/
00077   int still_running;          /* Is background url fetch still in progress */
00078 };
00079 
00080 typedef struct fcurl_data URL_FILE;
00081 
00082 /* exported functions */
00083 URL_FILE *url_fopen(const char *url, const char *operation);
00084 int url_fclose(URL_FILE *file);
00085 int url_feof(URL_FILE *file);
00086 size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file);
00087 char *url_fgets(char *ptr, size_t size, URL_FILE *file);
00088 void url_rewind(URL_FILE *file);
00089 
00090 /* we use a global one for convenience */
00091 CURLM *multi_handle;
00092 
00093 /* curl calls this routine to get more data */
00094 static size_t write_callback(char *buffer,
00095                              size_t size,
00096                              size_t nitems,
00097                              void *userp)
00098 {
00099   char *newbuff;
00100   size_t rembuff;
00101 
00102   URL_FILE *url = (URL_FILE *)userp;
00103   size *= nitems;
00104 
00105   rembuff=url->buffer_len - url->buffer_pos; /* remaining space in buffer */
00106 
00107   if(size > rembuff) {
00108     /* not enough space in buffer */
00109     newbuff=realloc(url->buffer, url->buffer_len + (size - rembuff));
00110     if(newbuff==NULL) {
00111       fprintf(stderr, "callback buffer grow failed\n");
00112       size=rembuff;
00113     }
00114     else {
00115       /* realloc succeeded increase buffer size*/
00116       url->buffer_len+=size - rembuff;
00117       url->buffer=newbuff;
00118     }
00119   }
00120 
00121   memcpy(&url->buffer[url->buffer_pos], buffer, size);
00122   url->buffer_pos += size;
00123 
00124   return size;
00125 }
00126 
00127 /* use to attempt to fill the read buffer up to requested number of bytes */
00128 static int fill_buffer(URL_FILE *file, size_t want)
00129 {
00130   fd_set fdread;
00131   fd_set fdwrite;
00132   fd_set fdexcep;
00133   struct timeval timeout;
00134   int rc;
00135   CURLMcode mc; /* curl_multi_fdset() return code */
00136 
00137   /* only attempt to fill buffer if transactions still running and buffer
00138    * doesn't exceed required size already
00139    */
00140   if((!file->still_running) || (file->buffer_pos > want))
00141     return 0;
00142 
00143   /* attempt to fill buffer */
00144   do {
00145     int maxfd = -1;
00146     long curl_timeo = -1;
00147 
00148     FD_ZERO(&fdread);
00149     FD_ZERO(&fdwrite);
00150     FD_ZERO(&fdexcep);
00151 
00152     /* set a suitable timeout to fail on */
00153     timeout.tv_sec = 60; /* 1 minute */
00154     timeout.tv_usec = 0;
00155 
00156     curl_multi_timeout(multi_handle, &curl_timeo);
00157     if(curl_timeo >= 0) {
00158       timeout.tv_sec = curl_timeo / 1000;
00159       if(timeout.tv_sec > 1)
00160         timeout.tv_sec = 1;
00161       else
00162         timeout.tv_usec = (curl_timeo % 1000) * 1000;
00163     }
00164 
00165     /* get file descriptors from the transfers */
00166     mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
00167 
00168     if(mc != CURLM_OK) {
00169       fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc);
00170       break;
00171     }
00172 
00173     /* On success the value of maxfd is guaranteed to be >= -1. We call
00174        select(maxfd + 1, ...); specially in case of (maxfd == -1) there are
00175        no fds ready yet so we call select(0, ...) --or Sleep() on Windows--
00176        to sleep 100ms, which is the minimum suggested value in the
00177        curl_multi_fdset() doc. */
00178 
00179     if(maxfd == -1) {
00180 #ifdef _WIN32
00181       Sleep(100);
00182       rc = 0;
00183 #else
00184       /* Portable sleep for platforms other than Windows. */
00185       struct timeval wait = { 0, 100 * 1000 }; /* 100ms */
00186       rc = select(0, NULL, NULL, NULL, &wait);
00187 #endif
00188     }
00189     else {
00190       /* Note that on some platforms 'timeout' may be modified by select().
00191          If you need access to the original value save a copy beforehand. */
00192       rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
00193     }
00194 
00195     switch(rc) {
00196     case -1:
00197       /* select error */
00198       break;
00199 
00200     case 0:
00201     default:
00202       /* timeout or readable/writable sockets */
00203       curl_multi_perform(multi_handle, &file->still_running);
00204       break;
00205     }
00206   } while(file->still_running && (file->buffer_pos < want));
00207   return 1;
00208 }
00209 
00210 /* use to remove want bytes from the front of a files buffer */
00211 static int use_buffer(URL_FILE *file, size_t want)
00212 {
00213   /* sort out buffer */
00214   if((file->buffer_pos - want) <=0) {
00215     /* ditch buffer - write will recreate */
00216     free(file->buffer);
00217     file->buffer=NULL;
00218     file->buffer_pos=0;
00219     file->buffer_len=0;
00220   }
00221   else {
00222     /* move rest down make it available for later */
00223     memmove(file->buffer,
00224             &file->buffer[want],
00225             (file->buffer_pos - want));
00226 
00227     file->buffer_pos -= want;
00228   }
00229   return 0;
00230 }
00231 
00232 URL_FILE *url_fopen(const char *url, const char *operation)
00233 {
00234   /* this code could check for URLs or types in the 'url' and
00235      basically use the real fopen() for standard files */
00236 
00237   URL_FILE *file;
00238   (void)operation;
00239 
00240   file = malloc(sizeof(URL_FILE));
00241   if(!file)
00242     return NULL;
00243 
00244   memset(file, 0, sizeof(URL_FILE));
00245 
00246   if((file->handle.file=fopen(url, operation)))
00247     file->type = CFTYPE_FILE; /* marked as URL */
00248 
00249   else {
00250     file->type = CFTYPE_CURL; /* marked as URL */
00251     file->handle.curl = curl_easy_init();
00252 
00253     curl_easy_setopt(file->handle.curl, CURLOPT_URL, url);
00254     curl_easy_setopt(file->handle.curl, CURLOPT_WRITEDATA, file);
00255     curl_easy_setopt(file->handle.curl, CURLOPT_VERBOSE, 0L);
00256     curl_easy_setopt(file->handle.curl, CURLOPT_WRITEFUNCTION, write_callback);
00257 
00258     if(!multi_handle)
00259       multi_handle = curl_multi_init();
00260 
00261     curl_multi_add_handle(multi_handle, file->handle.curl);
00262 
00263     /* lets start the fetch */
00264     curl_multi_perform(multi_handle, &file->still_running);
00265 
00266     if((file->buffer_pos == 0) && (!file->still_running)) {
00267       /* if still_running is 0 now, we should return NULL */
00268 
00269       /* make sure the easy handle is not in the multi handle anymore */
00270       curl_multi_remove_handle(multi_handle, file->handle.curl);
00271 
00272       /* cleanup */
00273       curl_easy_cleanup(file->handle.curl);
00274 
00275       free(file);
00276 
00277       file = NULL;
00278     }
00279   }
00280   return file;
00281 }
00282 
00283 int url_fclose(URL_FILE *file)
00284 {
00285   int ret=0;/* default is good return */
00286 
00287   switch(file->type) {
00288   case CFTYPE_FILE:
00289     ret=fclose(file->handle.file); /* passthrough */
00290     break;
00291 
00292   case CFTYPE_CURL:
00293     /* make sure the easy handle is not in the multi handle anymore */
00294     curl_multi_remove_handle(multi_handle, file->handle.curl);
00295 
00296     /* cleanup */
00297     curl_easy_cleanup(file->handle.curl);
00298     break;
00299 
00300   default: /* unknown or supported type - oh dear */
00301     ret=EOF;
00302     errno=EBADF;
00303     break;
00304   }
00305 
00306   free(file->buffer);/* free any allocated buffer space */
00307   free(file);
00308 
00309   return ret;
00310 }
00311 
00312 int url_feof(URL_FILE *file)
00313 {
00314   int ret=0;
00315 
00316   switch(file->type) {
00317   case CFTYPE_FILE:
00318     ret=feof(file->handle.file);
00319     break;
00320 
00321   case CFTYPE_CURL:
00322     if((file->buffer_pos == 0) && (!file->still_running))
00323       ret = 1;
00324     break;
00325 
00326   default: /* unknown or supported type - oh dear */
00327     ret=-1;
00328     errno=EBADF;
00329     break;
00330   }
00331   return ret;
00332 }
00333 
00334 size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file)
00335 {
00336   size_t want;
00337 
00338   switch(file->type) {
00339   case CFTYPE_FILE:
00340     want=fread(ptr, size, nmemb, file->handle.file);
00341     break;
00342 
00343   case CFTYPE_CURL:
00344     want = nmemb * size;
00345 
00346     fill_buffer(file, want);
00347 
00348     /* check if theres data in the buffer - if not fill_buffer()
00349      * either errored or EOF */
00350     if(!file->buffer_pos)
00351       return 0;
00352 
00353     /* ensure only available data is considered */
00354     if(file->buffer_pos < want)
00355       want = file->buffer_pos;
00356 
00357     /* xfer data to caller */
00358     memcpy(ptr, file->buffer, want);
00359 
00360     use_buffer(file, want);
00361 
00362     want = want / size;     /* number of items */
00363     break;
00364 
00365   default: /* unknown or supported type - oh dear */
00366     want=0;
00367     errno=EBADF;
00368     break;
00369 
00370   }
00371   return want;
00372 }
00373 
00374 char *url_fgets(char *ptr, size_t size, URL_FILE *file)
00375 {
00376   size_t want = size - 1;/* always need to leave room for zero termination */
00377   size_t loop;
00378 
00379   switch(file->type) {
00380   case CFTYPE_FILE:
00381     ptr = fgets(ptr, (int)size, file->handle.file);
00382     break;
00383 
00384   case CFTYPE_CURL:
00385     fill_buffer(file, want);
00386 
00387     /* check if theres data in the buffer - if not fill either errored or
00388      * EOF */
00389     if(!file->buffer_pos)
00390       return NULL;
00391 
00392     /* ensure only available data is considered */
00393     if(file->buffer_pos < want)
00394       want = file->buffer_pos;
00395 
00396     /*buffer contains data */
00397     /* look for newline or eof */
00398     for(loop=0;loop < want;loop++) {
00399       if(file->buffer[loop] == '\n') {
00400         want=loop+1;/* include newline */
00401         break;
00402       }
00403     }
00404 
00405     /* xfer data to caller */
00406     memcpy(ptr, file->buffer, want);
00407     ptr[want]=0;/* allways null terminate */
00408 
00409     use_buffer(file, want);
00410 
00411     break;
00412 
00413   default: /* unknown or supported type - oh dear */
00414     ptr=NULL;
00415     errno=EBADF;
00416     break;
00417   }
00418 
00419   return ptr;/*success */
00420 }
00421 
00422 void url_rewind(URL_FILE *file)
00423 {
00424   switch(file->type) {
00425   case CFTYPE_FILE:
00426     rewind(file->handle.file); /* passthrough */
00427     break;
00428 
00429   case CFTYPE_CURL:
00430     /* halt transaction */
00431     curl_multi_remove_handle(multi_handle, file->handle.curl);
00432 
00433     /* restart */
00434     curl_multi_add_handle(multi_handle, file->handle.curl);
00435 
00436     /* ditch buffer - write will recreate - resets stream pos*/
00437     free(file->buffer);
00438     file->buffer=NULL;
00439     file->buffer_pos=0;
00440     file->buffer_len=0;
00441 
00442     break;
00443 
00444   default: /* unknown or supported type - oh dear */
00445     break;
00446   }
00447 }
00448 
00449 #define FGETSFILE "fgets.test"
00450 #define FREADFILE "fread.test"
00451 #define REWINDFILE "rewind.test"
00452 
00453 /* Small main program to retrive from a url using fgets and fread saving the
00454  * output to two test files (note the fgets method will corrupt binary files if
00455  * they contain 0 chars */
00456 int main(int argc, char *argv[])
00457 {
00458   URL_FILE *handle;
00459   FILE *outf;
00460 
00461   size_t nread;
00462   char buffer[256];
00463   const char *url;
00464 
00465   if(argc < 2)
00466     url="http://192.168.7.3/testfile";/* default to testurl */
00467   else
00468     url=argv[1];/* use passed url */
00469 
00470   /* copy from url line by line with fgets */
00471   outf=fopen(FGETSFILE, "wb+");
00472   if(!outf) {
00473     perror("couldn't open fgets output file\n");
00474     return 1;
00475   }
00476 
00477   handle = url_fopen(url, "r");
00478   if(!handle) {
00479     printf("couldn't url_fopen() %s\n", url);
00480     fclose(outf);
00481     return 2;
00482   }
00483 
00484   while(!url_feof(handle)) {
00485     url_fgets(buffer, sizeof(buffer), handle);
00486     fwrite(buffer, 1, strlen(buffer), outf);
00487   }
00488 
00489   url_fclose(handle);
00490 
00491   fclose(outf);
00492 
00493 
00494   /* Copy from url with fread */
00495   outf=fopen(FREADFILE, "wb+");
00496   if(!outf) {
00497     perror("couldn't open fread output file\n");
00498     return 1;
00499   }
00500 
00501   handle = url_fopen("testfile", "r");
00502   if(!handle) {
00503     printf("couldn't url_fopen() testfile\n");
00504     fclose(outf);
00505     return 2;
00506   }
00507 
00508   do {
00509     nread = url_fread(buffer, 1, sizeof(buffer), handle);
00510     fwrite(buffer, 1, nread, outf);
00511   } while(nread);
00512 
00513   url_fclose(handle);
00514 
00515   fclose(outf);
00516 
00517 
00518   /* Test rewind */
00519   outf=fopen(REWINDFILE, "wb+");
00520   if(!outf) {
00521     perror("couldn't open fread output file\n");
00522     return 1;
00523   }
00524 
00525   handle = url_fopen("testfile", "r");
00526   if(!handle) {
00527     printf("couldn't url_fopen() testfile\n");
00528     fclose(outf);
00529     return 2;
00530   }
00531 
00532   nread = url_fread(buffer, 1, sizeof(buffer), handle);
00533   fwrite(buffer, 1, nread, outf);
00534   url_rewind(handle);
00535 
00536   buffer[0]='\n';
00537   fwrite(buffer, 1, 1, outf);
00538 
00539   nread = url_fread(buffer, 1, sizeof(buffer), handle);
00540   fwrite(buffer, 1, nread, outf);
00541 
00542   url_fclose(handle);
00543 
00544   fclose(outf);
00545 
00546   return 0;/* all done */
00547 }


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:02