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.
8 <<funopen>>, <<fropen>>, <<fwopen>>---open a stream with custom callbacks
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));
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">,
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
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
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
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.
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.
67 The functions <<fropen>> and <<fwopen>> are convenience macros around
68 <<funopen>> that only use the specified callback.
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.
77 This function is a newlib extension, copying the prototype from BSD.
78 It is not portable. See also the <<fopencookie>> interface from Linux.
80 Supporting OS subroutines required: <<sbrk>>.
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
);
94 typedef fpos_t (*funseek
)(void *_cookie
, fpos_t _off
, int _whence
);
96 typedef int (*funclose
)(void *_cookie
);
98 typedef struct funcookie
{
106 static _READ_WRITE_RETURN_TYPE
107 funreader (struct _reent
*ptr
,
110 _READ_WRITE_BUFSIZE_TYPE n
)
113 funcookie
*c
= (funcookie
*) cookie
;
115 if ((result
= c
->readfn (c
->cookie
, buf
, n
)) < 0 && errno
)
116 _REENT_ERRNO(ptr
) = errno
;
120 static _READ_WRITE_RETURN_TYPE
121 funwriter (struct _reent
*ptr
,
124 _READ_WRITE_BUFSIZE_TYPE n
)
127 funcookie
*c
= (funcookie
*) cookie
;
129 if ((result
= c
->writefn (c
->cookie
, buf
, n
)) < 0 && errno
)
130 _REENT_ERRNO(ptr
) = errno
;
135 funseeker (struct _reent
*ptr
,
140 funcookie
*c
= (funcookie
*) cookie
;
141 #ifndef __LARGE64_FILES
144 if ((result
= c
->seekfn (c
->cookie
, (fpos_t) off
, whence
)) < 0 && errno
)
145 _REENT_ERRNO(ptr
) = errno
;
146 #else /* __LARGE64_FILES */
149 if ((result
= c
->seekfn (c
->cookie
, (_fpos64_t
) off
, whence
)) < 0 && errno
)
150 _REENT_ERRNO(ptr
) = errno
;
151 else if ((_fpos_t
)result
!= result
)
153 _REENT_ERRNO(ptr
) = EOVERFLOW
;
156 #endif /* __LARGE64_FILES */
160 #ifdef __LARGE64_FILES
162 funseeker64 (struct _reent
*ptr
,
168 funcookie
*c
= (funcookie
*) cookie
;
170 if ((result
= c
->seekfn (c
->cookie
, off
, whence
)) < 0 && errno
)
171 _REENT_ERRNO(ptr
) = errno
;
174 #endif /* __LARGE64_FILES */
177 funcloser (struct _reent
*ptr
,
181 funcookie
*c
= (funcookie
*) cookie
;
185 if ((result
= c
->closefn (c
->cookie
)) < 0 && errno
)
186 _REENT_ERRNO(ptr
) = errno
;
193 _funopen_r (struct _reent
*ptr
,
203 if (!readfn
&& !writefn
)
205 _REENT_ERRNO(ptr
) = EINVAL
;
208 if ((fp
= __sfp (ptr
)) == NULL
)
210 if ((c
= (funcookie
*) _malloc_r (ptr
, sizeof *c
)) == NULL
)
212 _newlib_sfp_lock_start ();
213 fp
->_flags
= 0; /* release */
214 #ifndef __SINGLE_THREAD__
215 __lock_close_recursive (fp
->_lock
);
217 _newlib_sfp_lock_end ();
221 _newlib_flockfile_start (fp
);
223 c
->cookie
= (void *) cookie
; /* cast away const */
228 fp
->_read
= funreader
;
232 c
->writefn
= writefn
;
233 fp
->_write
= funwriter
;
245 c
->writefn
= writefn
;
246 fp
->_write
= funwriter
;
251 fp
->_seek
= seekfn
? funseeker
: NULL
;
252 #ifdef __LARGE64_FILES
253 fp
->_seek64
= seekfn
? funseeker64
: NULL
;
254 fp
->_flags
|= __SL64
;
256 c
->closefn
= closefn
;
257 fp
->_close
= funcloser
;
258 _newlib_flockfile_end (fp
);
264 funopen (const void *cookie
,
270 return _funopen_r (_REENT
, cookie
, readfn
, writefn
, seekfn
, closefn
);
272 #endif /* !_REENT_ONLY */