264 lines
7.1 KiB
C
264 lines
7.1 KiB
C
/* 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
|
|
|
|
SYNOPSIS
|
|
#include <stdio.h>
|
|
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 callbacks are registered via the structure:
|
|
|
|
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;
|
|
|
|
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>>.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#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,
|
|
void *cookie,
|
|
char *buf,
|
|
_READ_WRITE_BUFSIZE_TYPE 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,
|
|
void *cookie,
|
|
const char *buf,
|
|
_READ_WRITE_BUFSIZE_TYPE n)
|
|
{
|
|
int result;
|
|
fccookie *c = (fccookie *) cookie;
|
|
if (c->fp->_flags & __SAPP && c->fp->_seek)
|
|
{
|
|
#ifdef __LARGE64_FILES
|
|
c->fp->_seek64 (ptr, cookie, 0, SEEK_END);
|
|
#else
|
|
c->fp->_seek (ptr, 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, pos, whence),
|
|
struct _reent *ptr,
|
|
void *cookie,
|
|
_fpos_t pos,
|
|
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, pos, whence),
|
|
struct _reent *ptr,
|
|
void *cookie,
|
|
_fpos64_t pos,
|
|
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,
|
|
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,
|
|
void *cookie,
|
|
const char *mode,
|
|
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)
|
|
{
|
|
_newlib_sfp_lock_start ();
|
|
fp->_flags = 0; /* release */
|
|
#ifndef __SINGLE_THREAD__
|
|
__lock_close_recursive (fp->_lock);
|
|
#endif
|
|
_newlib_sfp_lock_end ();
|
|
return NULL;
|
|
}
|
|
|
|
_newlib_flockfile_start (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;
|
|
_newlib_flockfile_end (fp);
|
|
return fp;
|
|
}
|
|
|
|
#ifndef _REENT_ONLY
|
|
FILE *
|
|
_DEFUN(fopencookie, (cookie, mode, functions),
|
|
void *cookie,
|
|
const char *mode,
|
|
cookie_io_functions_t functions)
|
|
{
|
|
return _fopencookie_r (_REENT, cookie, mode, functions);
|
|
}
|
|
#endif /* !_REENT_ONLY */
|