summaryrefslogtreecommitdiffstats
path: root/lib/libc/stdio/open_memstream.c
diff options
context:
space:
mode:
authormpi <mpi@openbsd.org>2013-01-01 17:41:13 +0000
committermpi <mpi@openbsd.org>2013-01-01 17:41:13 +0000
commit1b5be6a08702f8e81266f60ba4466e14d6606c06 (patch)
tree5f15f90529c61a1f12f70028d59d8e1441b47cc5 /lib/libc/stdio/open_memstream.c
parentSwitch hp300 boot blocks to MI loadfile code. (diff)
downloadwireguard-openbsd-1b5be6a08702f8e81266f60ba4466e14d6606c06.tar.xz
wireguard-openbsd-1b5be6a08702f8e81266f60ba4466e14d6606c06.zip
Add an implementation based on tedu@'s design of fmemopen(3) and
open_memstream(3) so they can be polished in-tree. One of the manpages comes from NetBSD with some tweaks. Prodded by espie@, krw@, guenther@
Diffstat (limited to 'lib/libc/stdio/open_memstream.c')
-rw-r--r--lib/libc/stdio/open_memstream.c166
1 files changed, 166 insertions, 0 deletions
diff --git a/lib/libc/stdio/open_memstream.c b/lib/libc/stdio/open_memstream.c
new file mode 100644
index 00000000000..48ae2900f6d
--- /dev/null
+++ b/lib/libc/stdio/open_memstream.c
@@ -0,0 +1,166 @@
+/* $OpenBSD: open_memstream.c,v 1.1 2013/01/01 17:41:13 mpi Exp $ */
+/*
+ * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "local.h"
+
+struct state {
+ char *string; /* actual stream */
+ char **pbuf; /* point to the stream */
+ size_t *psize; /* point to min(pos, len) */
+ size_t pos; /* current position */
+ size_t size; /* allocated size */
+ size_t len; /* length of the data */
+};
+
+static int
+memstream_write(void *v, const char *b, int l)
+{
+ struct state *st = v;
+ int i;
+ char *p;
+
+ if (st->pos + l >= st->size) {
+ /* 1.6 is (very) close to the golden ratio. */
+ size_t sz = st->size * 8 / 5;
+
+ if (sz < st->pos + l + 1)
+ sz = st->pos + l + 1;
+ p = realloc(st->string, sz);
+ if (!p)
+ return (-1);
+ bzero(p + st->size, sz - st->size);
+ *st->pbuf = st->string = p;
+ st->size = sz;
+ }
+
+ for (i = 0; i < l; i++)
+ st->string[st->pos + i] = b[i];
+ st->pos += i;
+
+ if (st->pos > st->len) {
+ st->len = st->pos;
+ st->string[st->len] = '\0';
+ }
+
+ *st->psize = st->pos;
+
+ return (i);
+}
+
+static fpos_t
+memstream_seek(void *v, fpos_t pos, int w)
+{
+ struct state *st = v;
+ char *p;
+
+ switch (w) {
+ case SEEK_SET:
+ break;
+ case SEEK_CUR:
+ pos += st->pos;
+ break;
+ case SEEK_END:
+ pos += st->len;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (pos < 0)
+ return (-1);
+
+ st->pos = pos;
+
+ if (st->pos < st->len)
+ *st->psize = st->pos;
+ else
+ *st->psize = st->len;
+
+ return (pos);
+}
+
+static int
+memstream_close(void *v)
+{
+ struct state *st = v;
+ char *p;
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+
+ *st->psize = MIN(st->pos, st->len);
+ *st->pbuf = st->string;
+
+ free(st);
+
+ return (0);
+#undef MIN
+}
+
+FILE *
+open_memstream(char **pbuf, size_t *psize)
+{
+ struct state *st;
+ FILE *fp;
+
+ if (pbuf == NULL || psize == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((st = malloc(sizeof(*st))) == NULL)
+ return (NULL);
+
+ if ((fp = __sfp()) == NULL) {
+ free(st);
+ return (NULL);
+ }
+
+ if (*psize < 128)
+ st->size = 128;
+ else
+ st->size = *psize;
+
+ if ((st->string = calloc(1, st->size)) == NULL) {
+ free(st);
+ fp->_flags = 0;
+ return (NULL);
+ }
+
+ *st->string = '\0';
+ st->pos = 0;
+ st->len = 0;
+ st->pbuf = pbuf;
+ st->psize = psize;
+
+ *pbuf = st->string;
+ *psize = st->len;
+
+ fp->_flags = __SWR;
+ fp->_file = -1;
+ fp->_cookie = st;
+ fp->_read = NULL;
+ fp->_write = memstream_write;
+ fp->_seek = memstream_seek;
+ fp->_close = memstream_close;
+
+ return (fp);
+}