libc: Initial implementation of open_memstream.
authorVenkatesh Srinivas <me@endeavour.zapto.org>
Mon, 12 Sep 2011 05:12:47 +0000 (22:12 -0700)
committerVenkatesh Srinivas <me@endeavour.zapto.org>
Mon, 12 Sep 2011 05:12:47 +0000 (22:12 -0700)
open_memstream allows wrapping a buffer via the FILE * interface; the
buffer is dynamically allocated and automatically expanding. The interface
appeared in POSIX 2008.

This implementation doesn't validate seek offsets and doesn't currently
handle SEEK_END.

lib/libc/stdio/Makefile.inc
lib/libc/stdio/open_memstream.c [new file with mode: 0644]

index 41a130f..43d9256 100644 (file)
@@ -15,7 +15,7 @@ SRCS+=        __fpending.c _flock_stub.c \
        fseek.c fsetpos.c ftell.c funopen.c fvwrite.c fwalk.c fwide.c \
        fwprintf.c fwrite.c fwscanf.c getc.c getchar.c \
        getdelim.c getline.c gets.c getw.c \
-       getwc.c getwchar.c makebuf.c mktemp.c perror.c \
+       getwc.c getwchar.c makebuf.c mktemp.c open_memstream.c perror.c \
        printf-pos.c printf.c putc.c \
        putchar.c puts.c putw.c putwc.c putwchar.c refill.c remove.c rewind.c \
        rget.c scanf.c setbuf.c setbuffer.c setvbuf.c snprintf.c sprintf.c \
diff --git a/lib/libc/stdio/open_memstream.c b/lib/libc/stdio/open_memstream.c
new file mode 100644 (file)
index 0000000..a3011a4
--- /dev/null
@@ -0,0 +1,158 @@
+/*-
+ * Copyright (c) 2011 Venkatesh Srinivas, 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+struct memstream_cookie {
+       char **pub_buf;
+       size_t *pub_size;
+
+       char *head;
+       size_t pos;
+       size_t tail;
+};
+
+static void
+sync_pub_cookie(struct memstream_cookie *cp)
+{
+       *cp->pub_buf = cp->head;
+       *cp->pub_size = cp->tail; 
+}
+
+static int
+memstream_writefn(void *cookie, const char *buf, int len)
+{
+       struct memstream_cookie *c;
+       size_t reqsize;
+
+       c = cookie;
+
+       /* Write is contained within valid region */
+       if (c->pos + len < c->tail) {
+               bcopy(buf, &c->head[c->pos], len);
+               c->pos += len;
+               return (len);
+       }
+
+       /* Write results in resizing buffer */
+       reqsize = c->pos + len + 1;
+       c->head = reallocf(c->head, reqsize);
+       if (c->head == NULL) {
+               errno = ENOMEM;
+               return (0);
+       }
+
+       bcopy(buf, &c->head[c->pos], len);
+       
+       c->tail = c->pos + len;
+       c->pos = c->tail;
+       c->head[c->tail] = '\0';
+
+       sync_pub_cookie(c);
+
+       return (len);
+}
+
+static fpos_t
+memstream_seekfn(void *cookie, fpos_t pos, int whence)
+{
+       struct memstream_cookie *c;
+       
+       c = cookie;
+
+       /* XXX: Should validate SEEK_SET and SEEK_CUR positions */
+       /* XXX: What to do wrt SEEK_END? Is it relative to tail? to pos? */
+
+       switch(whence) {
+       case (SEEK_SET):
+               c->pos = pos;
+               return (c->pos);
+               break;
+       case (SEEK_CUR):
+               c->pos += pos;
+               return (c->pos);
+               break;
+       case (SEEK_END):
+       default:
+               errno = EINVAL;
+               return (fpos_t) -1;
+       }
+}
+
+static int
+memstream_closefn(void *cookie)
+{
+       struct memstream_cookie *c;
+
+       c = cookie;
+
+       sync_pub_cookie(c);
+
+       free(c);
+}
+
+FILE *
+open_memstream(char **bufp, size_t *sizep)
+{
+       FILE *fp;
+       struct memstream_cookie *c;
+
+       fp = NULL;
+       if (bufp == NULL || sizep == NULL) {
+               errno = EINVAL;
+               goto out;
+       }
+
+       c = malloc(sizeof(struct memstream_cookie));
+       if (c == NULL) {
+               errno = EINVAL;
+               goto out;
+       }
+
+       fp = funopen(c,
+                    NULL,
+                    memstream_writefn,
+                    memstream_seekfn,
+                    memstream_closefn
+                   );
+
+       if (fp == NULL) {
+               free(c);
+               errno = ENOMEM;
+               goto out;
+       }
+
+       c->pub_buf = bufp;
+       c->pub_size = sizep;
+       c->head = NULL;
+       c->tail = 0;
+       c->pos = 0;
+
+out:
+       return (fp);
+}