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 <<fopencookie>>---open a stream with custom callbacks
9 
10 INDEX
11 	fopencookie
12 
13 SYNOPSIS
14 	#include <stdio.h>
15 	FILE *fopencookie(const void *<[cookie]>, const char *<[mode]>,
16 			  cookie_io_functions_t <[functions]>);
17 
18 DESCRIPTION
19 <<fopencookie>> creates a <<FILE>> stream where I/O is performed using
20 custom callbacks.  The callbacks are registered via the structure:
21 
22 	typedef ssize_t (*cookie_read_function_t)(void *_cookie, char *_buf,
23 						  size_t _n);
24 	typedef ssize_t (*cookie_write_function_t)(void *_cookie,
25 						   const char *_buf, size_t _n);
26 	typedef int (*cookie_seek_function_t)(void *_cookie, off_t *_off,
27 					      int _whence);
28 	typedef int (*cookie_close_function_t)(void *_cookie);
29 
30 .	typedef struct
31 .	{
32 .		cookie_read_function_t	*read;
33 .		cookie_write_function_t *write;
34 .		cookie_seek_function_t	*seek;
35 .		cookie_close_function_t *close;
36 .	} cookie_io_functions_t;
37 
38 The stream is opened with <[mode]> treated as in <<fopen>>.  The
39 callbacks <[functions.read]> and <[functions.write]> may only be NULL
40 when <[mode]> does not require them.
41 
42 <[functions.read]> should return -1 on failure, or else the number of
43 bytes read (0 on EOF).  It is similar to <<read>>, except that
44 <[cookie]> will be passed as the first argument.
45 
46 <[functions.write]> should return -1 on failure, or else the number of
47 bytes written.  It is similar to <<write>>, except that <[cookie]>
48 will be passed as the first argument.
49 
50 <[functions.seek]> should return -1 on failure, and 0 on success, with
51 *<[_off]> set to the current file position.  It is a cross between
52 <<lseek>> and <<fseek>>, with the <[_whence]> argument interpreted in
53 the same manner.  A NULL <[functions.seek]> makes the stream behave
54 similarly to a pipe in relation to stdio functions that require
55 positioning.
56 
57 <[functions.close]> should return -1 on failure, or 0 on success.  It
58 is similar to <<close>>, except that <[cookie]> will be passed as the
59 first argument.  A NULL <[functions.close]> merely flushes all data
60 then lets <<fclose>> succeed.  A failed close will still invalidate
61 the stream.
62 
63 Read and write I/O functions are allowed to change the underlying
64 buffer on fully buffered or line buffered streams by calling
65 <<setvbuf>>.  They are also not required to completely fill or empty
66 the buffer.  They are not, however, allowed to change streams from
67 unbuffered to buffered or to change the state of the line buffering
68 flag.  They must also be prepared to have read or write calls occur on
69 buffers other than the one most recently specified.
70 
71 RETURNS
72 The return value is an open FILE pointer on success.  On error,
73 <<NULL>> is returned, and <<errno>> will be set to EINVAL if a
74 function pointer is missing or <[mode]> is invalid, ENOMEM if the
75 stream cannot be created, or EMFILE if too many streams are already
76 open.
77 
78 PORTABILITY
79 This function is a newlib extension, copying the prototype from Linux.
80 It is not portable.  See also the <<funopen>> interface from BSD.
81 
82 Supporting OS subroutines required: <<sbrk>>.
83 */
84 
85 #define _GNU_SOURCE
86 #include <stdio.h>
87 #include <errno.h>
88 #include <sys/lock.h>
89 #include "local.h"
90 
91 typedef struct fccookie {
92   void *cookie;
93   FILE *fp;
94   cookie_read_function_t *readfn;
95   cookie_write_function_t *writefn;
96   cookie_seek_function_t *seekfn;
97   cookie_close_function_t *closefn;
98 } fccookie;
99 
100 static ssize_t
fcreader(void * cookie,char * buf,size_t n)101 fcreader (
102        void *cookie,
103        char *buf,
104        size_t n)
105 {
106   int result;
107   fccookie *c = (fccookie *) cookie;
108   errno = 0;
109   if ((result = c->readfn (c->cookie, buf, n)) < 0 && errno)
110     _REENT_ERRNO(ptr) = errno;
111   return result;
112 }
113 
114 static ssize_t
fcwriter(void * cookie,const char * buf,size_t n)115 fcwriter (
116        void *cookie,
117        const char *buf,
118        size_t n)
119 {
120   int result;
121   fccookie *c = (fccookie *) cookie;
122   if (c->fp->_flags & __SAPP && c->fp->_seek)
123     {
124 #ifdef __LARGE64_FILES
125       c->fp->_seek64 (cookie, 0, SEEK_END);
126 #else
127       c->fp->_seek (cookie, 0, SEEK_END);
128 #endif
129     }
130   errno = 0;
131   if ((result = c->writefn (c->cookie, buf, n)) < 0 && errno)
132     _REENT_ERRNO(ptr) = errno;
133   return result;
134 }
135 
136 static _fpos_t
fcseeker(void * cookie,_fpos_t pos,int whence)137 fcseeker (
138        void *cookie,
139        _fpos_t pos,
140        int whence)
141 {
142   fccookie *c = (fccookie *) cookie;
143 #ifndef __LARGE64_FILES
144   off_t offset = (off_t) pos;
145 #else /* __LARGE64_FILES */
146   _off64_t offset = (_off64_t) pos;
147 #endif /* __LARGE64_FILES */
148 
149   errno = 0;
150   if (c->seekfn (c->cookie, &offset, whence) < 0 && errno)
151     _REENT_ERRNO(ptr) = errno;
152 #ifdef __LARGE64_FILES
153   else if ((_fpos_t)offset != offset)
154     {
155       _REENT_ERRNO(ptr) = EOVERFLOW;
156       offset = -1;
157     }
158 #endif /* __LARGE64_FILES */
159   return (_fpos_t) offset;
160 }
161 
162 #ifdef __LARGE64_FILES
163 static _fpos64_t
fcseeker64(void * cookie,_fpos64_t pos,int whence)164 fcseeker64 (
165        void *cookie,
166        _fpos64_t pos,
167        int whence)
168 {
169   _off64_t offset = (_off64_t) pos;
170   fccookie *c = (fccookie *) cookie;
171   errno = 0;
172   if (c->seekfn (c->cookie, &offset, whence) < 0 && errno)
173     _REENT_ERRNO(ptr) = errno;
174   return (_fpos64_t) offset;
175 }
176 #endif /* __LARGE64_FILES */
177 
178 static int
fccloser(void * cookie)179 fccloser (
180        void *cookie)
181 {
182   int result = 0;
183   fccookie *c = (fccookie *) cookie;
184   if (c->closefn)
185     {
186       errno = 0;
187       if ((result = c->closefn (c->cookie)) < 0 && errno)
188 	_REENT_ERRNO(ptr) = errno;
189     }
190   free (c);
191   return result;
192 }
193 
194 FILE *
fopencookie(void * cookie,const char * mode,cookie_io_functions_t functions)195 fopencookie (
196        void *cookie,
197        const char *mode,
198        cookie_io_functions_t functions)
199 {
200   FILE *fp;
201   fccookie *c;
202   int flags;
203   int dummy;
204 
205   if ((flags = __sflags (mode, &dummy)) == 0)
206     return NULL;
207   if (((flags & (__SRD | __SRW)) && !functions.read)
208       || ((flags & (__SWR | __SRW)) && !functions.write))
209     {
210       _REENT_ERRNO(ptr) = EINVAL;
211       return NULL;
212     }
213   if ((fp = __sfp ()) == NULL)
214     return NULL;
215   if ((c = (fccookie *) malloc (sizeof *c)) == NULL)
216     {
217       _newlib_sfp_lock_start ();
218       fp->_flags = 0;		/* release */
219 #ifndef __SINGLE_THREAD__
220       __lock_close_recursive (fp->_lock);
221 #endif
222       _newlib_sfp_lock_end ();
223       return NULL;
224     }
225 
226   _newlib_flockfile_start (fp);
227   fp->_file = -1;
228   fp->_flags = flags;
229   c->cookie = cookie;
230   c->fp = fp;
231   fp->_cookie = c;
232   c->readfn = functions.read;
233   fp->_read = fcreader;
234   c->writefn = functions.write;
235   fp->_write = fcwriter;
236   c->seekfn = functions.seek;
237   fp->_seek = functions.seek ? fcseeker : NULL;
238 #ifdef __LARGE64_FILES
239   fp->_seek64 = functions.seek ? fcseeker64 : NULL;
240   fp->_flags |= __SL64;
241 #endif
242   c->closefn = functions.close;
243   fp->_close = fccloser;
244   _newlib_flockfile_end (fp);
245   return fp;
246 }
247