fopen.c
上传用户:coffee44
上传日期:2018-10-23
资源大小:12304k
文件大小:14k
源码类别:

TAPI编程

开发平台:

Visual C++

  1. /*****************************************************************************
  2.  *
  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_ .
  7.  *
  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
  12.  *
  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.
  15.  *
  16.  * Coyright (c)2003 Simtec Electronics
  17.  *
  18.  * Re-implemented by Vincent Sanders <vince@kyllikki.org> with extensive
  19.  * reference to original curl example code
  20.  *
  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.
  31.  *
  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.
  42.  *
  43.  * This example requires libcurl 7.9.7 or later.
  44.  */
  45. #include <stdio.h>
  46. #include <string.h>
  47. #ifndef WIN32
  48. #  include <sys/time.h>
  49. #endif
  50. #include <stdlib.h>
  51. #include <errno.h>
  52. #include <curl/curl.h>
  53. enum fcurl_type_e { CFTYPE_NONE=0, CFTYPE_FILE=1, CFTYPE_CURL=2 };
  54. struct fcurl_data
  55. {
  56.     enum fcurl_type_e type;     /* type of handle */
  57.     union {
  58.         CURL *curl;
  59.         FILE *file;
  60.     } handle;                   /* handle */
  61.     char *buffer;               /* buffer to store cached data*/
  62.     int buffer_len;             /* currently allocated buffers length */
  63.     int buffer_pos;             /* end of data in buffer*/
  64.     int still_running;          /* Is background url fetch still in progress */
  65. };
  66. typedef struct fcurl_data URL_FILE;
  67. /* exported functions */
  68. URL_FILE *url_fopen(const char *url,const char *operation);
  69. int url_fclose(URL_FILE *file);
  70. int url_feof(URL_FILE *file);
  71. size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file);
  72. char * url_fgets(char *ptr, int size, URL_FILE *file);
  73. void url_rewind(URL_FILE *file);
  74. /* we use a global one for convenience */
  75. CURLM *multi_handle;
  76. /* curl calls this routine to get more data */
  77. static size_t
  78. write_callback(char *buffer,
  79.                size_t size,
  80.                size_t nitems,
  81.                void *userp)
  82. {
  83.     char *newbuff;
  84.     int rembuff;
  85.     URL_FILE *url = (URL_FILE *)userp;
  86.     size *= nitems;
  87.     rembuff=url->buffer_len - url->buffer_pos; /* remaining space in buffer */
  88.     if(size > rembuff)
  89.     {
  90.         /* not enough space in buffer */
  91.         newbuff=realloc(url->buffer,url->buffer_len + (size - rembuff));
  92.         if(newbuff==NULL)
  93.         {
  94.             fprintf(stderr,"callback buffer grow failedn");
  95.             size=rembuff;
  96.         }
  97.         else
  98.         {
  99.             /* realloc suceeded increase buffer size*/
  100.             url->buffer_len+=size - rembuff;
  101.             url->buffer=newbuff;
  102.             /*printf("Callback buffer grown to %d bytesn",url->buffer_len);*/
  103.         }
  104.     }
  105.     memcpy(&url->buffer[url->buffer_pos], buffer, size);
  106.     url->buffer_pos += size;
  107.     /*fprintf(stderr, "callback %d size bytesn", size);*/
  108.     return size;
  109. }
  110. /* use to attempt to fill the read buffer up to requested number of bytes */
  111. static int
  112. fill_buffer(URL_FILE *file,int want,int waittime)
  113. {
  114.     fd_set fdread;
  115.     fd_set fdwrite;
  116.     fd_set fdexcep;
  117.     int maxfd;
  118.     struct timeval timeout;
  119.     int rc;
  120.     /* only attempt to fill buffer if transactions still running and buffer
  121.      * doesnt exceed required size already
  122.      */
  123.     if((!file->still_running) || (file->buffer_pos > want))
  124.         return 0;
  125.     /* attempt to fill buffer */
  126.     do
  127.     {
  128.         FD_ZERO(&fdread);
  129.         FD_ZERO(&fdwrite);
  130.         FD_ZERO(&fdexcep);
  131.         /* set a suitable timeout to fail on */
  132.         timeout.tv_sec = 60; /* 1 minute */
  133.         timeout.tv_usec = 0;
  134.         /* get file descriptors from the transfers */
  135.         curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
  136.         /* In a real-world program you OF COURSE check the return code of the
  137.            function calls, *and* you make sure that maxfd is bigger than -1
  138.            so that the call to select() below makes sense! */
  139.         rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
  140.         switch(rc) {
  141.         case -1:
  142.             /* select error */
  143.             break;
  144.         case 0:
  145.             break;
  146.         default:
  147.             /* timeout or readable/writable sockets */
  148.             /* note we *could* be more efficient and not wait for
  149.              * CURLM_CALL_MULTI_PERFORM to clear here and check it on re-entry
  150.              * but that gets messy */
  151.             while(curl_multi_perform(multi_handle, &file->still_running) ==
  152.                   CURLM_CALL_MULTI_PERFORM);
  153.             break;
  154.         }
  155.     } while(file->still_running && (file->buffer_pos < want));
  156.     return 1;
  157. }
  158. /* use to remove want bytes from the front of a files buffer */
  159. static int
  160. use_buffer(URL_FILE *file,int want)
  161. {
  162.     /* sort out buffer */
  163.     if((file->buffer_pos - want) <=0)
  164.     {
  165.         /* ditch buffer - write will recreate */
  166.         if(file->buffer)
  167.             free(file->buffer);
  168.         file->buffer=NULL;
  169.         file->buffer_pos=0;
  170.         file->buffer_len=0;
  171.     }
  172.     else
  173.     {
  174.         /* move rest down make it available for later */
  175.         memmove(file->buffer,
  176.                 &file->buffer[want],
  177.                 (file->buffer_pos - want));
  178.         file->buffer_pos -= want;
  179.     }
  180.     return 0;
  181. }
  182. URL_FILE *
  183. url_fopen(const char *url,const char *operation)
  184. {
  185.     /* this code could check for URLs or types in the 'url' and
  186.        basicly use the real fopen() for standard files */
  187.     URL_FILE *file;
  188.     (void)operation;
  189.     file = malloc(sizeof(URL_FILE));
  190.     if(!file)
  191.         return NULL;
  192.     memset(file, 0, sizeof(URL_FILE));
  193.     if((file->handle.file=fopen(url,operation)))
  194.     {
  195.         file->type = CFTYPE_FILE; /* marked as URL */
  196.     }
  197.     else
  198.     {
  199.         file->type = CFTYPE_CURL; /* marked as URL */
  200.         file->handle.curl = curl_easy_init();
  201.         curl_easy_setopt(file->handle.curl, CURLOPT_URL, url);
  202.         curl_easy_setopt(file->handle.curl, CURLOPT_WRITEDATA, file);
  203.         curl_easy_setopt(file->handle.curl, CURLOPT_VERBOSE, 0L);
  204.         curl_easy_setopt(file->handle.curl, CURLOPT_WRITEFUNCTION, write_callback);
  205.         if(!multi_handle)
  206.             multi_handle = curl_multi_init();
  207.         curl_multi_add_handle(multi_handle, file->handle.curl);
  208.         /* lets start the fetch */
  209.         while(curl_multi_perform(multi_handle, &file->still_running) ==
  210.               CURLM_CALL_MULTI_PERFORM );
  211.         if((file->buffer_pos == 0) && (!file->still_running))
  212.         {
  213.             /* if still_running is 0 now, we should return NULL */
  214.             /* make sure the easy handle is not in the multi handle anymore */
  215.             curl_multi_remove_handle(multi_handle, file->handle.curl);
  216.             /* cleanup */
  217.             curl_easy_cleanup(file->handle.curl);
  218.             free(file);
  219.             file = NULL;
  220.         }
  221.     }
  222.     return file;
  223. }
  224. int
  225. url_fclose(URL_FILE *file)
  226. {
  227.     int ret=0;/* default is good return */
  228.     switch(file->type)
  229.     {
  230.     case CFTYPE_FILE:
  231.         ret=fclose(file->handle.file); /* passthrough */
  232.         break;
  233.     case CFTYPE_CURL:
  234.         /* make sure the easy handle is not in the multi handle anymore */
  235.         curl_multi_remove_handle(multi_handle, file->handle.curl);
  236.         /* cleanup */
  237.         curl_easy_cleanup(file->handle.curl);
  238.         break;
  239.     default: /* unknown or supported type - oh dear */
  240.         ret=EOF;
  241.         errno=EBADF;
  242.         break;
  243.     }
  244.     if(file->buffer)
  245.         free(file->buffer);/* free any allocated buffer space */
  246.     free(file);
  247.     return ret;
  248. }
  249. int
  250. url_feof(URL_FILE *file)
  251. {
  252.     int ret=0;
  253.     switch(file->type)
  254.     {
  255.     case CFTYPE_FILE:
  256.         ret=feof(file->handle.file);
  257.         break;
  258.     case CFTYPE_CURL:
  259.         if((file->buffer_pos == 0) && (!file->still_running))
  260.             ret = 1;
  261.         break;
  262.     default: /* unknown or supported type - oh dear */
  263.         ret=-1;
  264.         errno=EBADF;
  265.         break;
  266.     }
  267.     return ret;
  268. }
  269. size_t
  270. url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file)
  271. {
  272.     size_t want;
  273.     switch(file->type)
  274.     {
  275.     case CFTYPE_FILE:
  276.         want=fread(ptr,size,nmemb,file->handle.file);
  277.         break;
  278.     case CFTYPE_CURL:
  279.         want = nmemb * size;
  280.         fill_buffer(file,want,1);
  281.         /* check if theres data in the buffer - if not fill_buffer()
  282.          * either errored or EOF */
  283.         if(!file->buffer_pos)
  284.             return 0;
  285.         /* ensure only available data is considered */
  286.         if(file->buffer_pos < want)
  287.             want = file->buffer_pos;
  288.         /* xfer data to caller */
  289.         memcpy(ptr, file->buffer, want);
  290.         use_buffer(file,want);
  291.         want = want / size;     /* number of items - nb correct op - checked
  292.                                  * with glibc code*/
  293.         /*printf("(fread) return %d bytes %d leftn", want,file->buffer_pos);*/
  294.         break;
  295.     default: /* unknown or supported type - oh dear */
  296.         want=0;
  297.         errno=EBADF;
  298.         break;
  299.     }
  300.     return want;
  301. }
  302. char *
  303. url_fgets(char *ptr, int size, URL_FILE *file)
  304. {
  305.     int want = size - 1;/* always need to leave room for zero termination */
  306.     int loop;
  307.     switch(file->type)
  308.     {
  309.     case CFTYPE_FILE:
  310.         ptr = fgets(ptr,size,file->handle.file);
  311.         break;
  312.     case CFTYPE_CURL:
  313.         fill_buffer(file,want,1);
  314.         /* check if theres data in the buffer - if not fill either errored or
  315.          * EOF */
  316.         if(!file->buffer_pos)
  317.             return NULL;
  318.         /* ensure only available data is considered */
  319.         if(file->buffer_pos < want)
  320.             want = file->buffer_pos;
  321.         /*buffer contains data */
  322.         /* look for newline or eof */
  323.         for(loop=0;loop < want;loop++)
  324.         {
  325.             if(file->buffer[loop] == 'n')
  326.             {
  327.                 want=loop+1;/* include newline */
  328.                 break;
  329.             }
  330.         }
  331.         /* xfer data to caller */
  332.         memcpy(ptr, file->buffer, want);
  333.         ptr[want]=0;/* allways null terminate */
  334.         use_buffer(file,want);
  335.         /*printf("(fgets) return %d bytes %d leftn", want,file->buffer_pos);*/
  336.         break;
  337.     default: /* unknown or supported type - oh dear */
  338.         ptr=NULL;
  339.         errno=EBADF;
  340.         break;
  341.     }
  342.     return ptr;/*success */
  343. }
  344. void
  345. url_rewind(URL_FILE *file)
  346. {
  347.     switch(file->type)
  348.     {
  349.     case CFTYPE_FILE:
  350.         rewind(file->handle.file); /* passthrough */
  351.         break;
  352.     case CFTYPE_CURL:
  353.         /* halt transaction */
  354.         curl_multi_remove_handle(multi_handle, file->handle.curl);
  355.         /* restart */
  356.         curl_multi_add_handle(multi_handle, file->handle.curl);
  357.         /* ditch buffer - write will recreate - resets stream pos*/
  358.         if(file->buffer)
  359.             free(file->buffer);
  360.         file->buffer=NULL;
  361.         file->buffer_pos=0;
  362.         file->buffer_len=0;
  363.         break;
  364.     default: /* unknown or supported type - oh dear */
  365.         break;
  366.     }
  367. }
  368. /* Small main program to retrive from a url using fgets and fread saving the
  369.  * output to two test files (note the fgets method will corrupt binary files if
  370.  * they contain 0 chars */
  371. int
  372. main(int argc, char *argv[])
  373. {
  374.     URL_FILE *handle;
  375.     FILE *outf;
  376.     int nread;
  377.     char buffer[256];
  378.     const char *url;
  379.     if(argc < 2)
  380.     {
  381.         url="http://192.168.7.3/testfile";/* default to testurl */
  382.     }
  383.     else
  384.     {
  385.         url=argv[1];/* use passed url */
  386.     }
  387.     /* copy from url line by line with fgets */
  388.     outf=fopen("fgets.test","w+");
  389.     if(!outf)
  390.     {
  391.         perror("couldn't open fgets output filen");
  392.         return 1;
  393.     }
  394.     handle = url_fopen(url, "r");
  395.     if(!handle)
  396.     {
  397.         printf("couldn't url_fopen() %sn", url);
  398.         fclose(outf);
  399.         return 2;
  400.     }
  401.     while(!url_feof(handle))
  402.     {
  403.         url_fgets(buffer,sizeof(buffer),handle);
  404.         fwrite(buffer,1,strlen(buffer),outf);
  405.     }
  406.     url_fclose(handle);
  407.     fclose(outf);
  408.     /* Copy from url with fread */
  409.     outf=fopen("fread.test","w+");
  410.     if(!outf)
  411.     {
  412.         perror("couldn't open fread output filen");
  413.         return 1;
  414.     }
  415.     handle = url_fopen("testfile", "r");
  416.     if(!handle) {
  417.         printf("couldn't url_fopen() testfilen");
  418.         fclose(outf);
  419.         return 2;
  420.     }
  421.     do {
  422.         nread = url_fread(buffer, 1,sizeof(buffer), handle);
  423.         fwrite(buffer,1,nread,outf);
  424.     } while(nread);
  425.     url_fclose(handle);
  426.     fclose(outf);
  427.     /* Test rewind */
  428.     outf=fopen("rewind.test","w+");
  429.     if(!outf)
  430.     {
  431.         perror("couldn't open fread output filen");
  432.         return 1;
  433.     }
  434.     handle = url_fopen("testfile", "r");
  435.     if(!handle) {
  436.         printf("couldn't url_fopen() testfilen");
  437.         fclose(outf);
  438.         return 2;
  439.     }
  440.         nread = url_fread(buffer, 1,sizeof(buffer), handle);
  441.         fwrite(buffer,1,nread,outf);
  442.         url_rewind(handle);
  443.         buffer[0]='n';
  444.         fwrite(buffer,1,1,outf);
  445.         nread = url_fread(buffer, 1,sizeof(buffer), handle);
  446.         fwrite(buffer,1,nread,outf);
  447.     url_fclose(handle);
  448.     fclose(outf);
  449.     return 0;/* all done */
  450. }