[444] | 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 |
---|
| 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 | ptr->_errno = errno; |
---|
| 117 | return result; |
---|
| 118 | } |
---|
| 119 | |
---|
| 120 | static _READ_WRITE_RETURN_TYPE |
---|
| 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 | ptr->_errno = errno; |
---|
| 131 | return result; |
---|
| 132 | } |
---|
| 133 | |
---|
| 134 | static _fpos_t |
---|
| 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 | ptr->_errno = 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 | ptr->_errno = errno; |
---|
| 151 | else if ((_fpos_t)result != result) |
---|
| 152 | { |
---|
| 153 | ptr->_errno = EOVERFLOW; |
---|
| 154 | result = -1; |
---|
| 155 | } |
---|
| 156 | #endif /* __LARGE64_FILES */ |
---|
| 157 | return result; |
---|
| 158 | } |
---|
| 159 | |
---|
| 160 | #ifdef __LARGE64_FILES |
---|
| 161 | static _fpos64_t |
---|
| 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 | ptr->_errno = errno; |
---|
| 172 | return result; |
---|
| 173 | } |
---|
| 174 | #endif /* __LARGE64_FILES */ |
---|
| 175 | |
---|
| 176 | static int |
---|
| 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 | ptr->_errno = errno; |
---|
| 187 | } |
---|
| 188 | _free_r (ptr, c); |
---|
| 189 | return result; |
---|
| 190 | } |
---|
| 191 | |
---|
| 192 | FILE * |
---|
| 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 | ptr->_errno = 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 * |
---|
| 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 */ |
---|