summaryrefslogtreecommitdiff
path: root/lib/libc
diff options
context:
space:
mode:
authorMartin Pieuchot <mpi@cvs.openbsd.org>2013-01-01 17:41:14 +0000
committerMartin Pieuchot <mpi@cvs.openbsd.org>2013-01-01 17:41:14 +0000
commit6e376cffb5c86f5d05da2f7e0ecb7fd7f496d017 (patch)
tree22d93e3e9ed5e913bc4519adccc13497662475c2 /lib/libc
parent09483410d6c16f2e5c8c9bb95d292904a53b18d2 (diff)
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')
-rw-r--r--lib/libc/stdio/fmemopen.3197
-rw-r--r--lib/libc/stdio/fmemopen.c186
-rw-r--r--lib/libc/stdio/open_memstream.3106
-rw-r--r--lib/libc/stdio/open_memstream.c166
4 files changed, 655 insertions, 0 deletions
diff --git a/lib/libc/stdio/fmemopen.3 b/lib/libc/stdio/fmemopen.3
new file mode 100644
index 00000000000..2a996d66797
--- /dev/null
+++ b/lib/libc/stdio/fmemopen.3
@@ -0,0 +1,197 @@
+.\" $OpenBSD: fmemopen.3,v 1.1 2013/01/01 17:41:13 mpi Exp $
+.\" $NetBSD: fmemopen.3,v 1.5 2010/10/07 00:14:14 enami Exp $
+.\"
+.\" Copyright (c) 2010 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Christos Zoulas.
+.\"
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+.\"
+.Dd $Mdocdate: January 1 2013 $
+.Dt FMEMOPEN 3
+.Os
+.Sh NAME
+.Nm fmemopen
+.Nd open a stream that points to the given buffer
+.Sh SYNOPSIS
+.Fd #include <stdio.h>
+.Ft FILE *
+.Fn fmemopen "void *buffer" "size_t size" "const char *mode"
+.Sh DESCRIPTION
+The
+.Fn fmemopen
+function associates a stream with the given
+.Fa buffer
+and
+.Fa size .
+The
+.Fa buffer
+can be either
+.Dv NULL ,
+or must be of the given
+.Fa size .
+If the
+.Fa buffer
+is
+.Dv NULL ,
+a
+.Fa buffer
+of the given
+.Fa size
+will be dynamically allocated using
+.Xr malloc 3
+and released when
+.Xr fclose 3
+is called.
+.Pp
+The
+.Fa mode
+argument has the same meaning as in
+.Xr fopen 3 .
+.Pp
+The stream treats the buffer as it would treat a file tracking the current
+position to perform I/O operations.
+For example, in the beginning the stream points to the beginning of the buffer,
+unless
+.Dv a
+was specified in the
+.Fa mode
+argument, and then it points to the first
+.Dv NUL
+byte.
+If a
+.Dv NULL
+.Fa buffer
+was specified, then the stream will always point at the first byte of the
+.Fa buffer .
+.Pp
+The stream also keeps track of the
+.Fa size
+of the
+.Fa buffer .
+The
+.Fa size
+is initialized depending on the mode:
+.Bl -tag -width r/w+
+.It Dv r/r+
+Set to the
+.Fa size
+argument.
+.It Dv w/w+
+Set to
+.Dv 0 .
+.It Dv a/a+
+Set to the first
+.Dv NUL
+byte, or the
+.Fa size
+argument if one is not found.
+.El
+.Pp
+Read or write operations advance the buffer, but not to exceed the given
+.Fa size
+of the
+.Fa buffer .
+Trying to read beyond the
+.Fa size
+of the
+.Fa buffer
+results in
+.Dv EOF
+returned.
+.Dv NUL
+bytes are read normally.
+Trying to write beyond the
+.Fa size
+of the
+.Fa buffer
+has no effect.
+.Pp
+When a stream open for writing is either flushed or closed, a
+.Dv NUL
+byte is written at the current position or at the end of the current
+.Fa size
+as kept internally.
+.Sh RETURN VALUES
+Upon successful completion,
+.Fn fmemopen
+returns a
+.Dv FILE
+pointer.
+Otherwise,
+.Dv NULL
+is returned and the global variable
+.Va errno
+is set to indicate the error.
+.Sh ERRORS
+.Bl -tag -width Er
+.It Bq Er EINVAL
+The
+.Fa size
+was
+.Dv 0 ;
+or the
+.Fa mode
+argument is invalid;
+or the
+.Fa buffer
+argument is
+.Dv NULL
+and the
+.Fa mode
+argument does not specify a
+.Dv + .
+.El
+.Pp
+The
+.Fn fmemopen
+function
+may also fail and set
+.Va errno
+for any of the errors
+specified for the routine
+.Xr malloc 3 .
+.Sh SEE ALSO
+.Xr fclose 3 ,
+.Xr fflush 3 ,
+.Xr fopen 3 ,
+.Xr funopen 3 ,
+.Xr malloc 3
+.Sh STANDARDS
+The function
+.Fn fmemopen
+conform to
+.St -p1003.1-2008 .
+.Sh HISTORY
+The
+.Fn fmemopen
+function first appeared in
+.Ox 5.3 .
diff --git a/lib/libc/stdio/fmemopen.c b/lib/libc/stdio/fmemopen.c
new file mode 100644
index 00000000000..fd76f625b23
--- /dev/null
+++ b/lib/libc/stdio/fmemopen.c
@@ -0,0 +1,186 @@
+/* $OpenBSD: fmemopen.c,v 1.1 2013/01/01 17:41:13 mpi Exp $ */
+/*
+ * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
+ * Copyright (c) 2009 Ted Unangst
+ *
+ * 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 "local.h"
+
+struct state {
+ char *string; /* actual stream */
+ size_t pos; /* current position */
+ size_t size; /* allocated size */
+ size_t len; /* length of the data */
+};
+
+static int
+fmemopen_read(void *v, char *b, int l)
+{
+ struct state *st = v;
+ int i;
+
+ for (i = 0; i < l && i + st->pos < st->len; i++)
+ b[i] = st->string[st->pos + i];
+ st->pos += i;
+
+ return (i);
+}
+
+static int
+fmemopen_write(void *v, const char *b, int l)
+{
+ struct state *st = v;
+ int i;
+
+ for (i = 0; i < l && i + st->pos < st->size; i++)
+ st->string[st->pos + i] = b[i];
+ st->pos += i;
+
+ if (st->pos >= st->len) {
+ st->len = st->pos;
+
+ if (st->len == st->size)
+ st->string[st->len - 1] = '\0';
+ else
+ st->string[st->len] = '\0';
+ }
+
+ return (i);
+}
+
+static fpos_t
+fmemopen_seek(void *v, fpos_t pos, int whence)
+{
+ struct state *st = v;
+
+ switch (whence) {
+ case SEEK_SET:
+ break;
+ case SEEK_CUR:
+ pos += st->pos;
+ break;
+ case SEEK_END:
+ /*
+ * XXX The standard is not clear about where to seek
+ * from the end of the data or the end of the buffer.
+ */
+ pos += st->len;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (pos < 0 || pos > st->size)
+ return (-1);
+
+ st->pos = pos;
+
+ return (pos);
+}
+
+static int
+fmemopen_close(void *v)
+{
+ free(v);
+
+ return (0);
+}
+
+static int
+fmemopen_close_free(void *v)
+{
+ struct state *st = v;
+
+ free(st->string);
+ free(st);
+
+ return (0);
+}
+
+FILE *
+fmemopen(void *buf, size_t size, const char *mode)
+{
+ struct state *st;
+ FILE *fp;
+ int flags, oflags;
+
+ if (size == 0) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((flags = __sflags(mode, &oflags)) == 0) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if (buf == NULL && ((oflags & O_RDWR) == 0)) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((st = malloc(sizeof(*st))) == NULL)
+ return (NULL);
+
+ if ((fp = __sfp()) == NULL) {
+ free(st);
+ return (NULL);
+ }
+
+ st->pos = 0;
+ st->len = 0;
+ st->size = size;
+
+ if (buf == NULL) {
+ if ((st->string = malloc(size)) == NULL) {
+ free(st);
+ fp->_flags = 0;
+ return (NULL);
+ }
+ *st->string = '\0';
+ } else {
+ st->string = (char *)buf;
+
+ if ((oflags & O_WRONLY) == 0)
+ st->len = size;
+
+ if (oflags & O_TRUNC)
+ *st->string = '\0';
+
+ if (oflags & O_APPEND) {
+ char *p;
+
+ if ((p = memchr(st->string, '\0', size)) != NULL)
+ st->pos = st->len = (p - st->string);
+ else
+ st->pos = st->len = size;
+ }
+ }
+
+ fp->_flags = (short)flags;
+ fp->_file = -1;
+ fp->_cookie = (void *)st;
+ fp->_read = (flags & __SWR) ? NULL : fmemopen_read;
+ fp->_write = (flags & __SRD) ? NULL : fmemopen_write;
+ fp->_seek = fmemopen_seek;
+ fp->_close = (buf == NULL) ? fmemopen_close_free : fmemopen_close;
+
+ return (fp);
+}
diff --git a/lib/libc/stdio/open_memstream.3 b/lib/libc/stdio/open_memstream.3
new file mode 100644
index 00000000000..f081fd8849b
--- /dev/null
+++ b/lib/libc/stdio/open_memstream.3
@@ -0,0 +1,106 @@
+.\" $OpenBSD: open_memstream.3,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.
+.\"
+.Dd $Mdocdate: January 1 2013 $
+.Dt OPEN_MEMSTREAM 3
+.Os
+.Sh NAME
+.Nm open_memstream ,
+.Nm open_wmemstream
+.Nd open a memory buffer stream
+.Sh SYNOPSIS
+.Fd #include <stdio.h>
+.Ft FILE *
+.Fn open_memstream "char **pbuf" "size_t *psize"
+.Fd #include <wchar.h>
+.Ft FILE *
+.Fn open_wmemstream "wchar_t **pbuf" "size_t *psize"
+.Sh DESCRIPTION
+The
+.Fn open_memstream
+and
+.Fn open_wmemstream
+functions create a seekable byte-oriented, or respectively wide-oriented,
+stream for writing.
+A dynamically allocated buffer, using
+.Xr malloc 3 ,
+is then wrapped to the pointer referenced by
+.Fa pbuf
+and grows automatically as required.
+.Pp
+When the stream is either closed or flushed, the address of the buffer is
+stored in the pointer referenced by
+.Fa pbuf .
+At the same time the smaller of the current position and the buffer length is
+written in the variable pointed to by
+.Fa psize .
+This value represents the number of bytes, or respectively wide characters,
+contained in the buffer not including the terminating null character.
+.Pp
+The buffer memory should be release after the stream is closed.
+.Sh RETURN VALUES
+Upon successful completion,
+.Fn open_memstream
+and
+.Fn open_wmemstream
+return a
+.Dv FILE
+pointer.
+Otherwise,
+.Dv NULL
+is returned and the global variable
+.Va errno
+is set to indicate the error.
+.Sh ERRORS
+.Bl -tag -width Er
+.It Bq Er EINVAL
+The
+.Fa pbuf
+or the
+.Fa psize
+argument is
+.Dv NULL .
+.El
+.Pp
+The
+.Fn open_memstream
+and
+.Fn open_wmemstream
+functions
+may also fail and set
+.Va errno
+for any of the errors
+specified for the routine
+.Xr malloc 3 .
+.Sh SEE ALSO
+.Xr fmemopen 3 ,
+.Xr funopen 3 ,
+.Xr fopen 3 ,
+.Xr malloc 3
+.Sh STANDARDS
+The functions
+.Fn open_memstream
+and
+.Fn open_wmemstream ,
+conform to
+.St -p1003.1-2008 .
+.Sh HISTORY
+The
+.Fn open_memstream
+and
+.Fn open_wmemstream
+functions first appeared in
+.Ox 5.3 .
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);
+}