diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2013-01-01 17:41:14 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2013-01-01 17:41:14 +0000 |
commit | 6e376cffb5c86f5d05da2f7e0ecb7fd7f496d017 (patch) | |
tree | 22d93e3e9ed5e913bc4519adccc13497662475c2 /lib/libc/stdio | |
parent | 09483410d6c16f2e5c8c9bb95d292904a53b18d2 (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/stdio')
-rw-r--r-- | lib/libc/stdio/fmemopen.3 | 197 | ||||
-rw-r--r-- | lib/libc/stdio/fmemopen.c | 186 | ||||
-rw-r--r-- | lib/libc/stdio/open_memstream.3 | 106 | ||||
-rw-r--r-- | lib/libc/stdio/open_memstream.c | 166 |
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); +} |