summaryrefslogtreecommitdiff
path: root/libc/stdio/fmemopen.c
diff options
context:
space:
mode:
Diffstat (limited to 'libc/stdio/fmemopen.c')
-rw-r--r--libc/stdio/fmemopen.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/libc/stdio/fmemopen.c b/libc/stdio/fmemopen.c
new file mode 100644
index 000000000..ff03cb426
--- /dev/null
+++ b/libc/stdio/fmemopen.c
@@ -0,0 +1,176 @@
+/* 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 "_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;
+}