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