blob: 4a67376f5d4ac8dd8f5ccdaefbfe16e3a555bdc9 [file] [log] [blame]
/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org>
*
* GNU Library General Public License (LGPL) version 2 or later.
*
* Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details.
*/
#include <features.h>
#ifdef __USE_GNU
#include "_stdio.h"
#ifndef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__
#error no custom streams!
#endif
typedef struct {
size_t pos;
size_t len;
size_t eof;
int dynbuf;
unsigned char *buf;
FILE *fp;
} __fmo_cookie;
#define COOKIE ((__fmo_cookie *) cookie)
static ssize_t fmo_read(register void *cookie, char *buf, size_t bufsize)
{
size_t count = COOKIE->len - COOKIE->pos;
/* Note: 0 < bufsize < SSIZE_MAX because of _stdio_READ. */
if (!count) { /* EOF! */
return 0;
}
if (bufsize > count) {
bufsize = count;
}
memcpy(buf, COOKIE->buf + COOKIE->pos, bufsize);
COOKIE->pos += bufsize;
return bufsize;
}
static ssize_t fmo_write(register void *cookie, const char *buf, size_t bufsize)
{
size_t count;
/* Note: bufsize < SSIZE_MAX because of _stdio_WRITE. */
/* If appending, need to seek to end of file!!!! */
if (COOKIE->fp->__modeflags & __FLAG_APPEND) {
COOKIE->pos = COOKIE->eof;
}
count = COOKIE->len - COOKIE->pos;
if (bufsize > count) {
bufsize = count;
if (count == 0) { /* We're at the end of the buffer... */
__set_errno(EFBIG);
return -1;
}
}
memcpy(COOKIE->buf + COOKIE->pos, buf, bufsize);
COOKIE->pos += bufsize;
if (COOKIE->pos > COOKIE->eof) {
COOKIE->eof = COOKIE->pos;
if (bufsize < count) { /* New eof and still room in buffer? */
*(COOKIE->buf + COOKIE->pos) = 0;
}
}
return bufsize;
}
/* glibc doesn't allow seeking, but it has in-buffer seeks... we don't. */
static int fmo_seek(register void *cookie, __offmax_t *pos, int whence)
{
__offmax_t p = *pos;
/* Note: fseek already checks that whence is legal, so don't check here
* unless debugging. */
assert(((unsigned int) whence) <= 2);
if (whence != SEEK_SET) {
p += (whence == SEEK_CUR) ? COOKIE->pos : /* SEEK_END */ COOKIE->eof;
}
/* Note: glibc only allows seeking in the buffer. We'll actually restrict
* to the data. */
/* Check for offset < 0, offset > eof, or offset overflow... */
if (((uintmax_t) p) > COOKIE->eof) {
return -1;
}
COOKIE->pos = *pos = p;
return 0;
}
static int fmo_close(register void *cookie)
{
if (COOKIE->dynbuf) {
free(COOKIE->buf);
}
free(cookie);
return 0;
}
#undef COOKIE
static const cookie_io_functions_t _fmo_io_funcs = {
fmo_read, fmo_write, fmo_seek, fmo_close
};
/* TODO: If we have buffers enabled, it might be worthwile to add a pointer
* to the FILE in the cookie and have read, write, and seek operate directly
* on the buffer itself (ie replace the FILE buffer with the cookie buffer
* and update FILE bufstart, etc. whenever we seek). */
FILE *fmemopen(void *s, size_t len, const char *modes)
{
FILE *fp;
register __fmo_cookie *cookie;
size_t i;
if ((cookie = malloc(sizeof(__fmo_cookie))) != NULL) {
cookie->len = len;
cookie->eof = cookie->pos = 0; /* pos and eof adjusted below. */
cookie->dynbuf = 0;
if (((cookie->buf = s) == NULL) && (len > 0)) {
if ((cookie->buf = malloc(len)) == NULL) {
goto EXIT_cookie;
}
cookie->dynbuf = 1;
*cookie->buf = 0; /* If we're appending, treat as empty file. */
}
#ifndef __BCC__
fp = fopencookie(cookie, modes, _fmo_io_funcs);
#else
fp = fopencookie(cookie, modes, &_fmo_io_funcs);
#endif
/* Note: We don't need to worry about locking fp in the thread case
* as the only possible access would be a close or flush with
* nothing currently in the FILE's write buffer. */
if (fp != NULL) {
cookie->fp = fp;
if (fp->__modeflags & __FLAG_READONLY) {
cookie->eof = len;
}
if ((fp->__modeflags & __FLAG_APPEND) && (len > 0)) {
for (i = 0 ; i < len ; i++) {
if (cookie->buf[i] == 0) {
break;
}
}
cookie->eof = cookie->pos = i; /* Adjust eof and pos. */
}
__STDIO_STREAM_VALIDATE(fp);
return fp;
}
}
if (!s) {
free(cookie->buf);
}
EXIT_cookie:
free(cookie);
return NULL;
}
#endif