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 #define _DEFAULT_SOURCE
84 #include <stdio.h>
85 #include <errno.h>
86 #include <sys/lock.h>
87 #include "local.h"
88 
89 typedef int (*funread)(void *_cookie, char *_buf, size_t _n);
90 typedef int (*funwrite)(void *_cookie, const char *_buf,
91 			size_t _n);
92 #ifdef __LARGE64_FILES
93 typedef _fpos64_t (*funseek)(void *_cookie, _fpos64_t _off, int _whence);
94 #else
95 typedef fpos_t (*funseek)(void *_cookie, fpos_t _off, int _whence);
96 #endif
97 typedef int (*funclose)(void *_cookie);
98 
99 typedef struct funcookie {
100   void *cookie;
101   funread readfn;
102   funwrite writefn;
103   funseek seekfn;
104   funclose closefn;
105 } funcookie;
106 
107 static ssize_t
funreader(void * cookie,char * buf,size_t n)108 funreader (
109        void *cookie,
110        char *buf,
111        size_t 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     _REENT_ERRNO(ptr) = errno;
118   return result;
119 }
120 
121 static ssize_t
funwriter(void * cookie,const char * buf,size_t n)122 funwriter (
123        void *cookie,
124        const char *buf,
125        size_t n)
126 {
127   int result;
128   funcookie *c = (funcookie *) cookie;
129   errno = 0;
130   if ((result = c->writefn (c->cookie, buf, n)) < 0 && errno)
131     _REENT_ERRNO(ptr) = errno;
132   return result;
133 }
134 
135 static _fpos_t
funseeker(void * cookie,_fpos_t off,int whence)136 funseeker (
137        void *cookie,
138        _fpos_t off,
139        int whence)
140 {
141   funcookie *c = (funcookie *) cookie;
142 #ifndef __LARGE64_FILES
143   fpos_t result;
144   errno = 0;
145   if ((result = c->seekfn (c->cookie, (fpos_t) off, whence)) < 0 && errno)
146     _REENT_ERRNO(ptr) = errno;
147 #else /* __LARGE64_FILES */
148   _fpos64_t result;
149   errno = 0;
150   if ((result = c->seekfn (c->cookie, (_fpos64_t) off, whence)) < 0 && errno)
151     _REENT_ERRNO(ptr) = errno;
152   else if ((_fpos_t)result != result)
153     {
154       _REENT_ERRNO(ptr) = EOVERFLOW;
155       result = -1;
156     }
157 #endif /* __LARGE64_FILES */
158   return result;
159 }
160 
161 #ifdef __LARGE64_FILES
162 static _fpos64_t
funseeker64(void * cookie,_fpos64_t off,int whence)163 funseeker64 (
164        void *cookie,
165        _fpos64_t off,
166        int whence)
167 {
168   _fpos64_t result;
169   funcookie *c = (funcookie *) cookie;
170   errno = 0;
171   if ((result = c->seekfn (c->cookie, off, whence)) < 0 && errno)
172     _REENT_ERRNO(ptr) = errno;
173   return result;
174 }
175 #endif /* __LARGE64_FILES */
176 
177 static int
funcloser(void * cookie)178 funcloser (
179        void *cookie)
180 {
181   int result = 0;
182   funcookie *c = (funcookie *) cookie;
183   if (c->closefn)
184     {
185       errno = 0;
186       if ((result = c->closefn (c->cookie)) < 0 && errno)
187 	_REENT_ERRNO(ptr) = errno;
188     }
189   free (c);
190   return result;
191 }
192 
193 FILE *
funopen(const void * cookie,funread readfn,funwrite writefn,funseek seekfn,funclose closefn)194 funopen (
195        const void *cookie,
196        funread readfn,
197        funwrite writefn,
198        funseek seekfn,
199        funclose closefn)
200 {
201   FILE *fp;
202   funcookie *c;
203 
204   if (!readfn && !writefn)
205     {
206       _REENT_ERRNO(ptr) = EINVAL;
207       return NULL;
208     }
209   if ((fp = __sfp ()) == NULL)
210     return NULL;
211   if ((c = (funcookie *) malloc (sizeof *c)) == NULL)
212     {
213       _newlib_sfp_lock_start ();
214       fp->_flags = 0;		/* release */
215 #ifndef __SINGLE_THREAD__
216       __lock_close_recursive (fp->_lock);
217 #endif
218       _newlib_sfp_lock_end ();
219       return NULL;
220     }
221 
222   _newlib_flockfile_start (fp);
223   fp->_file = -1;
224   c->cookie = (void *) cookie; /* cast away const */
225   fp->_cookie = c;
226   if (readfn)
227     {
228       c->readfn = readfn;
229       fp->_read = funreader;
230       if (writefn)
231 	{
232 	  fp->_flags = __SRW;
233 	  c->writefn = writefn;
234 	  fp->_write = funwriter;
235 	}
236       else
237 	{
238 	  fp->_flags = __SRD;
239 	  c->writefn = NULL;
240 	  fp->_write = NULL;
241 	}
242     }
243   else
244     {
245       fp->_flags = __SWR;
246       c->writefn = writefn;
247       fp->_write = funwriter;
248       c->readfn = NULL;
249       fp->_read = NULL;
250     }
251   c->seekfn = seekfn;
252   fp->_seek = seekfn ? funseeker : NULL;
253 #ifdef __LARGE64_FILES
254   fp->_seek64 = seekfn ? funseeker64 : NULL;
255   fp->_flags |= __SL64;
256 #endif
257   c->closefn = closefn;
258   fp->_close = funcloser;
259   _newlib_flockfile_end (fp);
260   return fp;
261 }
262