diff options
author | Eric Blake <eblake@redhat.com> | 2007-06-04 18:10:17 +0000 |
---|---|---|
committer | Eric Blake <eblake@redhat.com> | 2007-06-04 18:10:17 +0000 |
commit | 17c61d6a2c4868260c1ef3043e03f86de14f6a08 (patch) | |
tree | c795768b8f7d0fa0048f5bd77f6dad9c5ce069e1 /newlib/libc/stdio/fopencookie.c | |
parent | 6a3b4eb69a7d2b1e1c19a8baef42ce6dc3d61a86 (diff) | |
download | newlib-17c61d6a2c4868260c1ef3043e03f86de14f6a08.zip newlib-17c61d6a2c4868260c1ef3043e03f86de14f6a08.tar.gz newlib-17c61d6a2c4868260c1ef3043e03f86de14f6a08.tar.bz2 |
Implement funopen, fopencookie.
* libc/include/sys/reent.h (struct __sFILE, struct __sFILE64):
Switch to reentrant callbacks.
* libc/include/stdio.h (funopen): Fix declaration.
(fopencookie): Declare.
* libc/stdio/local.h (__sread, __swrite, __sseek, __sclose)
(__sseek64, __swrite64): Fix prototypes.
[__SCLE]: Pull in setmode declaration.
* libc/stdio/stdio.c (__sread, __swrite, __sseek, __sclose): Fix
reentrancy.
* libc/stdio64/stdio64.c (__sseek64_r, __swrite64_r): Delete.
(__sseek64, __swrite64): Fix reentrancy.
* libc/stdio/fseek.c (_fseek_r): Account for overflow, and fix
reentrancy.
* libc/stdio/ftell.c (_ftell_r): Likewise.
* libc/stdio/flags.c (__sflags): Don't lose __SAPP on "a+".
* libc/stdio/fclose.c (_fclose_r): Fix reentrancy.
* libc/stdio/freopen.c (_freopen_r): Likewise.
* libc/stdio/fvwrite.c (__sfvwrite_r): Likewise.
* libc/stdio/refill.c (__srefill_r): Likewise.
* libc/stdio/siscanf.c (eofread): Likewise.
* libc/stdio/sscanf.c (eofread): Likewise.
* libc/stdio/vsiscanf.c (eofread1): Likewise.
* libc/stdio/vsscanf.c (eofread1): Likewise.
* libc/stdio64/freopen64.c (_freopen64_r): Likewise.
* libc/stdio64/fseeko64.c (_fseeko64_r): Likewise.
* libc/stdio64/ftello64.c (_ftello64_r): Likewise.
* libc/stdio/fflush.c (fflush): Improve reentrancy, although more
could be fixed.
* libc/stdio/fopencookie.c (_fopencookie_r, fopencookie): New file.
* libc/stdio/funopen.c (_funopen_r, funopen): New file.
* libc/stdio/Makefile.am (ELIX_4_SOURCES, CHEWOUT_FILES): Build
new files.
* libc/stdio/Makefile.in: Regenerate.
Diffstat (limited to 'newlib/libc/stdio/fopencookie.c')
-rw-r--r-- | newlib/libc/stdio/fopencookie.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/newlib/libc/stdio/fopencookie.c b/newlib/libc/stdio/fopencookie.c new file mode 100644 index 0000000..581441b --- /dev/null +++ b/newlib/libc/stdio/fopencookie.c @@ -0,0 +1,258 @@ +/* Copyright (C) 2007 Eric Blake + * Permission to use, copy, modify, and distribute this software + * is freely granted, provided that this notice is preserved. + */ + +/* +FUNCTION +<<fopencookie>>---open a stream with custom callbacks + +INDEX + fopencookie + +ANSI_SYNOPSIS + #include <stdio.h> + typedef ssize_t (*cookie_read_function_t)(void *_cookie, char *_buf, + size_t _n); + typedef ssize_t (*cookie_write_function_t)(void *_cookie, + const char *_buf, size_t _n); + typedef int (*cookie_seek_function_t)(void *_cookie, off_t *_off, + int _whence); + typedef int (*cookie_close_function_t)(void *_cookie); + typedef struct + { + cookie_read_function_t *read; + cookie_write_function_t *write; + cookie_seek_function_t *seek; + cookie_close_function_t *close; + } cookie_io_functions_t; + FILE *fopencookie(const void *<[cookie]>, const char *<[mode]>, + cookie_io_functions_t <[functions]>); + +DESCRIPTION +<<fopencookie>> creates a <<FILE>> stream where I/O is performed using +custom callbacks. The stream is opened with <[mode]> treated as in +<<fopen>>. The callbacks <[functions.read]> and <[functions.write]> +may only be NULL when <[mode]> does not require them. + +<[functions.read]> should return -1 on failure, or else the number of +bytes read (0 on EOF). It is similar to <<read>>, except that +<[cookie]> will be passed as the first argument. + +<[functions.write]> should return -1 on failure, or else the number of +bytes written. It is similar to <<write>>, except that <[cookie]> +will be passed as the first argument. + +<[functions.seek]> should return -1 on failure, and 0 on success, with +*<[_off]> set to the current file position. It is a cross between +<<lseek>> and <<fseek>>, with the <[_whence]> argument interpreted in +the same manner. A NULL <[functions.seek]> makes the stream behave +similarly to a pipe in relation to stdio functions that require +positioning. + +<[functions.close]> should return -1 on failure, or 0 on success. It +is similar to <<close>>, except that <[cookie]> will be passed as the +first argument. A NULL <[functions.close]> merely flushes all data +then lets <<fclose>> succeed. A failed close will still invalidate +the stream. + +Read and write I/O functions are allowed to change the underlying +buffer on fully buffered or line buffered streams by calling +<<setvbuf>>. They are also not required to completely fill or empty +the buffer. They are not, however, allowed to change streams from +unbuffered to buffered or to change the state of the line buffering +flag. They must also be prepared to have read or write calls occur on +buffers other than the one most recently specified. + +RETURNS +The return value is an open FILE pointer on success. On error, +<<NULL>> is returned, and <<errno>> will be set to EINVAL if a +function pointer is missing or <[mode]> is invalid, ENOMEM if the +stream cannot be created, or EMFILE if too many streams are already +open. + +PORTABILITY +This function is a newlib extension, copying the prototype from Linux. +It is not portable. See also the <<funopen>> interface from BSD. + +Supporting OS subroutines required: <<sbrk>>. +*/ + +#include <stdio.h> +#include <errno.h> +#include <sys/lock.h> +#include "local.h" + +typedef struct fccookie { + void *cookie; + FILE *fp; + cookie_read_function_t *readfn; + cookie_write_function_t *writefn; + cookie_seek_function_t *seekfn; + cookie_close_function_t *closefn; +} fccookie; + +static _READ_WRITE_RETURN_TYPE +_DEFUN(fcreader, (ptr, cookie, buf, n), + struct _reent *ptr _AND + void *cookie _AND + char *buf _AND + int n) +{ + int result; + fccookie *c = (fccookie *) cookie; + errno = 0; + if ((result = c->readfn (c->cookie, buf, n)) < 0 && errno) + ptr->_errno = errno; + return result; +} + +static _READ_WRITE_RETURN_TYPE +_DEFUN(fcwriter, (ptr, cookie, buf, n), + struct _reent *ptr _AND + void *cookie _AND + const char *buf _AND + int n) +{ + int result; + fccookie *c = (fccookie *) cookie; + if (c->fp->_flags & __SAPP && c->fp->_seek) + { +#ifdef __LARGE64_FILES + c->fp->_seek64 (ptr, c->fp->_cookie, 0, SEEK_END); +#else + c->fp->_seek (ptr, c->fp->_cookie, 0, SEEK_END); +#endif + } + errno = 0; + if ((result = c->writefn (c->cookie, buf, n)) < 0 && errno) + ptr->_errno = errno; + return result; +} + +static _fpos_t +_DEFUN(fcseeker, (ptr, cookie, off, whence), + struct _reent *ptr _AND + void *cookie _AND + _fpos_t pos _AND + int whence) +{ + fccookie *c = (fccookie *) cookie; +#ifndef __LARGE64_FILES + off_t offset = (off_t) pos; +#else /* __LARGE64_FILES */ + _off64_t offset = (_off64_t) pos; +#endif /* __LARGE64_FILES */ + + errno = 0; + if (c->seekfn (c->cookie, &offset, whence) < 0 && errno) + ptr->_errno = errno; +#ifdef __LARGE64_FILES + else if ((_fpos_t)offset != offset) + { + ptr->_errno = EOVERFLOW; + offset = -1; + } +#endif /* __LARGE64_FILES */ + return (_fpos_t) offset; +} + +#ifdef __LARGE64_FILES +static _fpos64_t +_DEFUN(fcseeker64, (ptr, cookie, off, whence), + struct _reent *ptr _AND + void *cookie _AND + _fpos64_t pos _AND + int whence) +{ + _off64_t offset; + fccookie *c = (fccookie *) cookie; + errno = 0; + if (c->seekfn (c->cookie, &offset, whence) < 0 && errno) + ptr->_errno = errno; + return (_fpos64_t) offset; +} +#endif /* __LARGE64_FILES */ + +static int +_DEFUN(fccloser, (ptr, cookie), + struct _reent *ptr _AND + void *cookie) +{ + int result = 0; + fccookie *c = (fccookie *) cookie; + if (c->closefn) + { + errno = 0; + if ((result = c->closefn (c->cookie)) < 0 && errno) + ptr->_errno = errno; + } + _free_r (ptr, c); + return result; +} + +FILE * +_DEFUN(_fopencookie_r, (ptr, cookie, mode, functions), + struct _reent *ptr _AND + void *cookie _AND + const char *mode _AND + cookie_io_functions_t functions) +{ + FILE *fp; + fccookie *c; + int flags; + int dummy; + + if ((flags = __sflags (ptr, mode, &dummy)) == 0) + return NULL; + if (((flags & (__SRD | __SRW)) && !functions.read) + || ((flags & (__SWR | __SRW)) && !functions.write)) + { + ptr->_errno = EINVAL; + return NULL; + } + if ((fp = __sfp (ptr)) == NULL) + return NULL; + if ((c = (fccookie *) _malloc_r (ptr, sizeof *c)) == NULL) + { + __sfp_lock_acquire (); + fp->_flags = 0; /* release */ +#ifndef __SINGLE_THREAD__ + __lock_close_recursive (fp->_lock); +#endif + __sfp_lock_release (); + return NULL; + } + + _flockfile (fp); + fp->_file = -1; + fp->_flags = flags; + c->cookie = cookie; + c->fp = fp; + fp->_cookie = c; + c->readfn = functions.read; + fp->_read = fcreader; + c->writefn = functions.write; + fp->_write = fcwriter; + c->seekfn = functions.seek; + fp->_seek = functions.seek ? fcseeker : NULL; +#ifdef __LARGE64_FILES + fp->_seek64 = functions.seek ? fcseeker64 : NULL; + fp->_flags |= __SL64; +#endif + c->closefn = functions.close; + fp->_close = fccloser; + _funlockfile (fp); + return fp; +} + +#ifndef _REENT_ONLY +FILE * +_DEFUN(fopencookie, (cookie, mode, functions), + void *cookie _AND + const char *mode _AND + cookie_io_functions_t functions) +{ + return _fopencookie_r (_REENT, cookie, mode, functions); +} +#endif /* !_REENT_ONLY */ |