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 _READ_WRITE_RETURN_TYPE |
---|
101 | fcreader (struct _reent *ptr, |
---|
102 | void *cookie, |
---|
103 | char *buf, |
---|
104 | _READ_WRITE_BUFSIZE_TYPE 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 | ptr->_errno = errno; |
---|
111 | return result; |
---|
112 | } |
---|
113 | |
---|
114 | static _READ_WRITE_RETURN_TYPE |
---|
115 | fcwriter (struct _reent *ptr, |
---|
116 | void *cookie, |
---|
117 | const char *buf, |
---|
118 | _READ_WRITE_BUFSIZE_TYPE 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 (ptr, cookie, 0, SEEK_END); |
---|
126 | #else |
---|
127 | c->fp->_seek (ptr, cookie, 0, SEEK_END); |
---|
128 | #endif |
---|
129 | } |
---|
130 | errno = 0; |
---|
131 | if ((result = c->writefn (c->cookie, buf, n)) < 0 && errno) |
---|
132 | ptr->_errno = errno; |
---|
133 | return result; |
---|
134 | } |
---|
135 | |
---|
136 | static _fpos_t |
---|
137 | fcseeker (struct _reent *ptr, |
---|
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 | ptr->_errno = errno; |
---|
152 | #ifdef __LARGE64_FILES |
---|
153 | else if ((_fpos_t)offset != offset) |
---|
154 | { |
---|
155 | ptr->_errno = EOVERFLOW; |
---|
156 | offset = -1; |
---|
157 | } |
---|
158 | #endif /* __LARGE64_FILES */ |
---|
159 | return (_fpos_t) offset; |
---|
160 | } |
---|
161 | |
---|
162 | #ifdef __LARGE64_FILES |
---|
163 | static _fpos64_t |
---|
164 | fcseeker64 (struct _reent *ptr, |
---|
165 | void *cookie, |
---|
166 | _fpos64_t pos, |
---|
167 | int whence) |
---|
168 | { |
---|
169 | _off64_t offset; |
---|
170 | fccookie *c = (fccookie *) cookie; |
---|
171 | errno = 0; |
---|
172 | if (c->seekfn (c->cookie, &offset, whence) < 0 && errno) |
---|
173 | ptr->_errno = errno; |
---|
174 | return (_fpos64_t) offset; |
---|
175 | } |
---|
176 | #endif /* __LARGE64_FILES */ |
---|
177 | |
---|
178 | static int |
---|
179 | fccloser (struct _reent *ptr, |
---|
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 | ptr->_errno = errno; |
---|
189 | } |
---|
190 | _free_r (ptr, c); |
---|
191 | return result; |
---|
192 | } |
---|
193 | |
---|
194 | FILE * |
---|
195 | _fopencookie_r (struct _reent *ptr, |
---|
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 (ptr, mode, &dummy)) == 0) |
---|
206 | return NULL; |
---|
207 | if (((flags & (__SRD | __SRW)) && !functions.read) |
---|
208 | || ((flags & (__SWR | __SRW)) && !functions.write)) |
---|
209 | { |
---|
210 | ptr->_errno = EINVAL; |
---|
211 | return NULL; |
---|
212 | } |
---|
213 | if ((fp = __sfp (ptr)) == NULL) |
---|
214 | return NULL; |
---|
215 | if ((c = (fccookie *) _malloc_r (ptr, 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 | |
---|
248 | #ifndef _REENT_ONLY |
---|
249 | FILE * |
---|
250 | fopencookie (void *cookie, |
---|
251 | const char *mode, |
---|
252 | cookie_io_functions_t functions) |
---|
253 | { |
---|
254 | return _fopencookie_r (_REENT, cookie, mode, functions); |
---|
255 | } |
---|
256 | #endif /* !_REENT_ONLY */ |
---|