Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /* Copyright (C) 2007 Eric Blake
  2.  * Permission to use, copy, modify, and distribute this software
  3.  * is freely granted, provided that this notice is preserved.
  4.  */
  5.  
  6. /*
  7. FUNCTION
  8. <<funopen>>, <<fropen>>, <<fwopen>>---open a stream with custom callbacks
  9.  
  10. INDEX
  11.         funopen
  12. INDEX
  13.         fropen
  14. INDEX
  15.         fwopen
  16.  
  17. ANSI_SYNOPSIS
  18.         #include <stdio.h>
  19.         FILE *funopen(const void *<[cookie]>,
  20.                       int (*<[readfn]>) (void *cookie, char *buf, int n),
  21.                       int (*<[writefn]>) (void *cookie, const char *buf, int n),
  22.                       fpos_t (*<[seekfn]>) (void *cookie, fpos_t off, int whence),
  23.                       int (*<[closefn]>) (void *cookie));
  24.         FILE *fropen(const void *<[cookie]>,
  25.                      int (*<[readfn]>) (void *cookie, char *buf, int n));
  26.         FILE *fwopen(const void *<[cookie]>,
  27.                      int (*<[writefn]>) (void *cookie, const char *buf, int n));
  28.  
  29. DESCRIPTION
  30. <<funopen>> creates a <<FILE>> stream where I/O is performed using
  31. custom callbacks.  At least one of <[readfn]> and <[writefn]> must be
  32. provided, which determines whether the stream behaves with mode <"r">,
  33. <"w">, or <"r+">.
  34.  
  35. <[readfn]> should return -1 on failure, or else the number of bytes
  36. read (0 on EOF).  It is similar to <<read>>, except that <int> rather
  37. than <size_t> bounds a transaction size, and <[cookie]> will be passed
  38. as the first argument.  A NULL <[readfn]> makes attempts to read the
  39. stream fail.
  40.  
  41. <[writefn]> should return -1 on failure, or else the number of bytes
  42. written.  It is similar to <<write>>, except that <int> rather than
  43. <size_t> bounds a transaction size, and <[cookie]> will be passed as
  44. the first argument.  A NULL <[writefn]> makes attempts to write the
  45. stream fail.
  46.  
  47. <[seekfn]> should return (fpos_t)-1 on failure, or else the current
  48. file position.  It is similar to <<lseek>>, except that <[cookie]>
  49. will be passed as the first argument.  A NULL <[seekfn]> makes the
  50. stream behave similarly to a pipe in relation to stdio functions that
  51. require positioning.  This implementation assumes fpos_t and off_t are
  52. the same type.
  53.  
  54. <[closefn]> should return -1 on failure, or 0 on success.  It is
  55. similar to <<close>>, except that <[cookie]> will be passed as the
  56. first argument.  A NULL <[closefn]> merely flushes all data then lets
  57. <<fclose>> succeed.  A failed close will still invalidate the stream.
  58.  
  59. Read and write I/O functions are allowed to change the underlying
  60. buffer on fully buffered or line buffered streams by calling
  61. <<setvbuf>>.  They are also not required to completely fill or empty
  62. the buffer.  They are not, however, allowed to change streams from
  63. unbuffered to buffered or to change the state of the line buffering
  64. flag.  They must also be prepared to have read or write calls occur on
  65. buffers other than the one most recently specified.
  66.  
  67. The functions <<fropen>> and <<fwopen>> are convenience macros around
  68. <<funopen>> that only use the specified callback.
  69.  
  70. RETURNS
  71. The return value is an open FILE pointer on success.  On error,
  72. <<NULL>> is returned, and <<errno>> will be set to EINVAL if a
  73. function pointer is missing, ENOMEM if the stream cannot be created,
  74. or EMFILE if too many streams are already open.
  75.  
  76. PORTABILITY
  77. This function is a newlib extension, copying the prototype from BSD.
  78. It is not portable.  See also the <<fopencookie>> interface from Linux.
  79.  
  80. Supporting OS subroutines required: <<sbrk>>.
  81. */
  82.  
  83. #include <stdio.h>
  84. #include <errno.h>
  85. #include <sys/lock.h>
  86. #include "local.h"
  87.  
  88. typedef int (*funread)(void *_cookie, char *_buf, _READ_WRITE_BUFSIZE_TYPE _n);
  89. typedef int (*funwrite)(void *_cookie, const char *_buf,
  90.                         _READ_WRITE_BUFSIZE_TYPE _n);
  91. #ifdef __LARGE64_FILES
  92. typedef _fpos64_t (*funseek)(void *_cookie, _fpos64_t _off, int _whence);
  93. #else
  94. typedef fpos_t (*funseek)(void *_cookie, fpos_t _off, int _whence);
  95. #endif
  96. typedef int (*funclose)(void *_cookie);
  97.  
  98. typedef struct funcookie {
  99.   void *cookie;
  100.   funread readfn;
  101.   funwrite writefn;
  102.   funseek seekfn;
  103.   funclose closefn;
  104. } funcookie;
  105.  
  106. static _READ_WRITE_RETURN_TYPE
  107. _DEFUN(funreader, (ptr, cookie, buf, n),
  108.        struct _reent *ptr _AND
  109.        void *cookie _AND
  110.        char *buf _AND
  111.        _READ_WRITE_BUFSIZE_TYPE n)
  112. {
  113.   int result;
  114.   funcookie *c = (funcookie *) cookie;
  115.   errno = 0;
  116.   if ((result = c->readfn (c->cookie, buf, n)) < 0 && errno)
  117.     ptr->_errno = errno;
  118.   return result;
  119. }
  120.  
  121. static _READ_WRITE_RETURN_TYPE
  122. _DEFUN(funwriter, (ptr, cookie, buf, n),
  123.        struct _reent *ptr _AND
  124.        void *cookie _AND
  125.        const char *buf _AND
  126.        _READ_WRITE_BUFSIZE_TYPE n)
  127. {
  128.   int result;
  129.   funcookie *c = (funcookie *) cookie;
  130.   errno = 0;
  131.   if ((result = c->writefn (c->cookie, buf, n)) < 0 && errno)
  132.     ptr->_errno = errno;
  133.   return result;
  134. }
  135.  
  136. static _fpos_t
  137. _DEFUN(funseeker, (ptr, cookie, off, whence),
  138.        struct _reent *ptr _AND
  139.        void *cookie _AND
  140.        _fpos_t off _AND
  141.        int whence)
  142. {
  143.   funcookie *c = (funcookie *) cookie;
  144. #ifndef __LARGE64_FILES
  145.   fpos_t result;
  146.   errno = 0;
  147.   if ((result = c->seekfn (c->cookie, (fpos_t) off, whence)) < 0 && errno)
  148.     ptr->_errno = errno;
  149. #else /* __LARGE64_FILES */
  150.   _fpos64_t result;
  151.   errno = 0;
  152.   if ((result = c->seekfn (c->cookie, (_fpos64_t) off, whence)) < 0 && errno)
  153.     ptr->_errno = errno;
  154.   else if ((_fpos_t)result != result)
  155.     {
  156.       ptr->_errno = EOVERFLOW;
  157.       result = -1;
  158.     }
  159. #endif /* __LARGE64_FILES */
  160.   return result;
  161. }
  162.  
  163. #ifdef __LARGE64_FILES
  164. static _fpos64_t
  165. _DEFUN(funseeker64, (ptr, cookie, off, whence),
  166.        struct _reent *ptr _AND
  167.        void *cookie _AND
  168.        _fpos64_t off _AND
  169.        int whence)
  170. {
  171.   _fpos64_t result;
  172.   funcookie *c = (funcookie *) cookie;
  173.   errno = 0;
  174.   if ((result = c->seekfn (c->cookie, off, whence)) < 0 && errno)
  175.     ptr->_errno = errno;
  176.   return result;
  177. }
  178. #endif /* __LARGE64_FILES */
  179.  
  180. static int
  181. _DEFUN(funcloser, (ptr, cookie),
  182.        struct _reent *ptr _AND
  183.        void *cookie)
  184. {
  185.   int result = 0;
  186.   funcookie *c = (funcookie *) cookie;
  187.   if (c->closefn)
  188.     {
  189.       errno = 0;
  190.       if ((result = c->closefn (c->cookie)) < 0 && errno)
  191.         ptr->_errno = errno;
  192.     }
  193.   _free_r (ptr, c);
  194.   return result;
  195. }
  196.  
  197. FILE *
  198. _DEFUN(_funopen_r, (ptr, cookie, readfn, writefn, seekfn, closefn),
  199.        struct _reent *ptr _AND
  200.        const void *cookie _AND
  201.        funread readfn _AND
  202.        funwrite writefn _AND
  203.        funseek seekfn _AND
  204.        funclose closefn)
  205. {
  206.   FILE *fp;
  207.   funcookie *c;
  208.  
  209.   if (!readfn && !writefn)
  210.     {
  211.       ptr->_errno = EINVAL;
  212.       return NULL;
  213.     }
  214.   if ((fp = __sfp (ptr)) == NULL)
  215.     return NULL;
  216.   if ((c = (funcookie *) _malloc_r (ptr, sizeof *c)) == NULL)
  217.     {
  218.       _newlib_sfp_lock_start ();
  219.       fp->_flags = 0;           /* release */
  220. #ifndef __SINGLE_THREAD__
  221.       __lock_close_recursive (fp->_lock);
  222. #endif
  223.       _newlib_sfp_lock_end ();
  224.       return NULL;
  225.     }
  226.  
  227.   _newlib_flockfile_start (fp);
  228.   fp->_file = -1;
  229.   c->cookie = (void *) cookie; /* cast away const */
  230.   fp->_cookie = c;
  231.   if (readfn)
  232.     {
  233.       c->readfn = readfn;
  234.       fp->_read = funreader;
  235.       if (writefn)
  236.         {
  237.           fp->_flags = __SRW;
  238.           c->writefn = writefn;
  239.           fp->_write = funwriter;
  240.         }
  241.       else
  242.         {
  243.           fp->_flags = __SRD;
  244.           c->writefn = NULL;
  245.           fp->_write = NULL;
  246.         }
  247.     }
  248.   else
  249.     {
  250.       fp->_flags = __SWR;
  251.       c->writefn = writefn;
  252.       fp->_write = funwriter;
  253.       c->readfn = NULL;
  254.       fp->_read = NULL;
  255.     }
  256.   c->seekfn = seekfn;
  257.   fp->_seek = seekfn ? funseeker : NULL;
  258. #ifdef __LARGE64_FILES
  259.   fp->_seek64 = seekfn ? funseeker64 : NULL;
  260.   fp->_flags |= __SL64;
  261. #endif
  262.   c->closefn = closefn;
  263.   fp->_close = funcloser;
  264.   _newlib_flockfile_end (fp);
  265.   return fp;
  266. }
  267.  
  268. #ifndef _REENT_ONLY
  269. FILE *
  270. _DEFUN(funopen, (cookie, readfn, writefn, seekfn, closefn),
  271.        const void *cookie _AND
  272.        funread readfn _AND
  273.        funwrite writefn _AND
  274.        funseek seekfn _AND
  275.        funclose closefn)
  276. {
  277.   return _funopen_r (_REENT, cookie, readfn, writefn, seekfn, closefn);
  278. }
  279. #endif /* !_REENT_ONLY */
  280.