| /* 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 |