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 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
funreader(struct _reent * ptr,void * cookie,char * buf,_READ_WRITE_BUFSIZE_TYPE n)107 funreader (struct _reent *ptr,
108        void *cookie,
109        char *buf,
110        _READ_WRITE_BUFSIZE_TYPE n)
111 {
112   int result;
113   funcookie *c = (funcookie *) cookie;
114   errno = 0;
115   if ((result = c->readfn (c->cookie, buf, n)) < 0 && errno)
116     __errno_r(ptr) = errno;
117   return result;
118 }
119 
120 static _READ_WRITE_RETURN_TYPE
funwriter(struct _reent * ptr,void * cookie,const char * buf,_READ_WRITE_BUFSIZE_TYPE n)121 funwriter (struct _reent *ptr,
122        void *cookie,
123        const char *buf,
124        _READ_WRITE_BUFSIZE_TYPE n)
125 {
126   int result;
127   funcookie *c = (funcookie *) cookie;
128   errno = 0;
129   if ((result = c->writefn (c->cookie, buf, n)) < 0 && errno)
130     __errno_r(ptr) = errno;
131   return result;
132 }
133 
134 static _fpos_t
funseeker(struct _reent * ptr,void * cookie,_fpos_t off,int whence)135 funseeker (struct _reent *ptr,
136        void *cookie,
137        _fpos_t off,
138        int whence)
139 {
140   funcookie *c = (funcookie *) cookie;
141 #ifndef __LARGE64_FILES
142   fpos_t result;
143   errno = 0;
144   if ((result = c->seekfn (c->cookie, (fpos_t) off, whence)) < 0 && errno)
145     __errno_r(ptr) = errno;
146 #else /* __LARGE64_FILES */
147   _fpos64_t result;
148   errno = 0;
149   if ((result = c->seekfn (c->cookie, (_fpos64_t) off, whence)) < 0 && errno)
150     __errno_r(ptr) = errno;
151   else if ((_fpos_t)result != result)
152     {
153       __errno_r(ptr) = EOVERFLOW;
154       result = -1;
155     }
156 #endif /* __LARGE64_FILES */
157   return result;
158 }
159 
160 #ifdef __LARGE64_FILES
161 static _fpos64_t
funseeker64(struct _reent * ptr,void * cookie,_fpos64_t off,int whence)162 funseeker64 (struct _reent *ptr,
163        void *cookie,
164        _fpos64_t off,
165        int whence)
166 {
167   _fpos64_t result;
168   funcookie *c = (funcookie *) cookie;
169   errno = 0;
170   if ((result = c->seekfn (c->cookie, off, whence)) < 0 && errno)
171     __errno_r(ptr) = errno;
172   return result;
173 }
174 #endif /* __LARGE64_FILES */
175 
176 static int
funcloser(struct _reent * ptr,void * cookie)177 funcloser (struct _reent *ptr,
178        void *cookie)
179 {
180   int result = 0;
181   funcookie *c = (funcookie *) cookie;
182   if (c->closefn)
183     {
184       errno = 0;
185       if ((result = c->closefn (c->cookie)) < 0 && errno)
186 	__errno_r(ptr) = errno;
187     }
188   _free_r (ptr, c);
189   return result;
190 }
191 
192 FILE *
_funopen_r(struct _reent * ptr,const void * cookie,funread readfn,funwrite writefn,funseek seekfn,funclose closefn)193 _funopen_r (struct _reent *ptr,
194        const void *cookie,
195        funread readfn,
196        funwrite writefn,
197        funseek seekfn,
198        funclose closefn)
199 {
200   FILE *fp;
201   funcookie *c;
202 
203   if (!readfn && !writefn)
204     {
205       __errno_r(ptr) = EINVAL;
206       return NULL;
207     }
208   if ((fp = __sfp (ptr)) == NULL)
209     return NULL;
210   if ((c = (funcookie *) _malloc_r (ptr, sizeof *c)) == NULL)
211     {
212       _newlib_sfp_lock_start ();
213       fp->_flags = 0;		/* release */
214 #ifndef __SINGLE_THREAD__
215       __lock_close_recursive (fp->_lock);
216 #endif
217       _newlib_sfp_lock_end ();
218       return NULL;
219     }
220 
221   _newlib_flockfile_start (fp);
222   fp->_file = -1;
223   c->cookie = (void *) cookie; /* cast away const */
224   fp->_cookie = c;
225   if (readfn)
226     {
227       c->readfn = readfn;
228       fp->_read = funreader;
229       if (writefn)
230 	{
231 	  fp->_flags = __SRW;
232 	  c->writefn = writefn;
233 	  fp->_write = funwriter;
234 	}
235       else
236 	{
237 	  fp->_flags = __SRD;
238 	  c->writefn = NULL;
239 	  fp->_write = NULL;
240 	}
241     }
242   else
243     {
244       fp->_flags = __SWR;
245       c->writefn = writefn;
246       fp->_write = funwriter;
247       c->readfn = NULL;
248       fp->_read = NULL;
249     }
250   c->seekfn = seekfn;
251   fp->_seek = seekfn ? funseeker : NULL;
252 #ifdef __LARGE64_FILES
253   fp->_seek64 = seekfn ? funseeker64 : NULL;
254   fp->_flags |= __SL64;
255 #endif
256   c->closefn = closefn;
257   fp->_close = funcloser;
258   _newlib_flockfile_end (fp);
259   return fp;
260 }
261 
262 #ifndef _REENT_ONLY
263 FILE *
funopen(const void * cookie,funread readfn,funwrite writefn,funseek seekfn,funclose closefn)264 funopen (const void *cookie,
265        funread readfn,
266        funwrite writefn,
267        funseek seekfn,
268        funclose closefn)
269 {
270   return _funopen_r (_REENT, cookie, readfn, writefn, seekfn, closefn);
271 }
272 #endif /* !_REENT_ONLY */
273