Module Name: src Committed By: christos Date: Mon Oct 13 00:40:36 UTC 2014
Modified Files: src/lib/libc/stdio: Makefile.inc Added Files: src/lib/libc/stdio: open_memstream.3 open_memstream.c open_wmemstream.c Log Message: PR/49279: Justin Cormack: add open_memstream To generate a diff of this commit: cvs rdiff -u -r1.45 -r1.46 src/lib/libc/stdio/Makefile.inc cvs rdiff -u -r0 -r1.1 src/lib/libc/stdio/open_memstream.3 \ src/lib/libc/stdio/open_memstream.c src/lib/libc/stdio/open_wmemstream.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/libc/stdio/Makefile.inc diff -u src/lib/libc/stdio/Makefile.inc:1.45 src/lib/libc/stdio/Makefile.inc:1.46 --- src/lib/libc/stdio/Makefile.inc:1.45 Wed Jun 18 13:50:55 2014 +++ src/lib/libc/stdio/Makefile.inc Sun Oct 12 20:40:36 2014 @@ -1,5 +1,5 @@ # from: @(#)Makefile.inc 5.7 (Berkeley) 6/27/91 -# $NetBSD: Makefile.inc,v 1.45 2014/06/18 17:50:55 christos Exp $ +# $NetBSD: Makefile.inc,v 1.46 2014/10/13 00:40:36 christos Exp $ # stdio sources .PATH: ${.CURDIR}/stdio @@ -21,7 +21,7 @@ SRCS+= clrerr.c dprintf.c fclose.c fdope vasprintf.c vdprintf.c vfprintf.c vfscanf.c vfwprintf.c vfwscanf.c \ vprintf.c vscanf.c vsnprintf.c vsnprintf_ss.c vsscanf.c vswprintf.c \ vswscanf.c vwprintf.c vwscanf.c wbuf.c wprintf.c wscanf.c wsetup.c -SRCS+= fmemopen.c +SRCS+= fmemopen.c open_memstream.c open_wmemstream.c .if !defined(AUDIT) SRCS+= gets.c vsprintf.c tempnam.c tmpnam.c mktemp.c @@ -31,7 +31,7 @@ MAN+= fclose.3 ferror.3 fflush.3 fgetln. flockfile.3 fmemopen.3 fopen.3 fparseln.3 fputs.3 fputws.3 fread.3 \ fseek.3 funopen.3 fwide.3 getc.3 getdelim.3 getwc.3 mktemp.3 printf.3 \ putc.3 putwc.3 remove.3 scanf.3 setbuf.3 stdio.3 tmpnam.3 \ - ungetc.3 ungetwc.3 wprintf.3 wscanf.3 + ungetc.3 ungetwc.3 wprintf.3 wscanf.3 open_memstream.3 MLINKS+=ferror.3 clearerr.3 ferror.3 feof.3 ferror.3 fileno.3 MLINKS+=fflush.3 fpurge.3 @@ -66,3 +66,4 @@ MLINKS+=wprintf.3 fwprintf.3 wprintf.3 s MLINKS+=wprintf.3 vwprintf.3 wprintf.3 vfwprintf.3 wprintf.3 vswprintf.3 MLINKS+=wscanf.3 fwscanf.3 wscanf.3 swscanf.3 wscanf.3 vwscanf.3 MLINKS+=wscanf.3 vswscanf.3 wscanf.3 vfwscanf.3 +MLINKS+=open_memstream.3 open_wmemstream.3 Added files: Index: src/lib/libc/stdio/open_memstream.3 diff -u /dev/null src/lib/libc/stdio/open_memstream.3:1.1 --- /dev/null Sun Oct 12 20:40:36 2014 +++ src/lib/libc/stdio/open_memstream.3 Sun Oct 12 20:40:36 2014 @@ -0,0 +1,156 @@ +.\" $NetBSD: open_memstream.3,v 1.1 2014/10/13 00:40:36 christos Exp $ +.\" Copyright (c) 2013 Advanced Computing Technologies LLC +.\" Written by: John H. Baldwin <j...@freebsd.org> +.\" 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. +.\" +.\" $FreeBSD: head/lib/libc/stdio/open_memstream.3 247415 2013-02-27 20:09:25Z joel $ +.\" +.Dd October 12, 2014 +.Dt OPEN_MEMSTREAM 3 +.Os +.Sh NAME +.Nm open_memstream , +.Nm open_wmemstream +.Nd dynamic memory buffer stream open functions +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In stdio.h +.Ft FILE * +.Fn open_memstream "char **bufp" "size_t **sizep" +.In wchar.h +.Ft FILE * +.Fn open_wmemstream "wchar_t **bufp" "size_t **sizep" +.Sh DESCRIPTION +The +.Fn open_memstream +and +.Fn open_wmemstream +functions create a write-only, seekable stream backed by a dynamically +allocated memory buffer. +The +.Fn open_memstream +function creates a byte-oriented stream, +while the +.Fn open_wmemstream +function creates a wide-oriented stream. +.Pp +Each stream maintains a current position and size. +Initially, +the position and size are set to zero. +Each write begins at the current position and advances it the number of +successfully written bytes for +.Fn open_memstream +or wide characters for +.Fn open_wmemstream . +If a write moves the current position beyond the length of the buffer, +the length of the buffer is extended and a null character is appended to the +buffer. +.Pp +A stream's buffer always contains a null character at the end of the buffer +that is not included in the current length. +.Pp +If a stream's current position is moved beyond the current length via a +seek operation and a write is performed, +the characters between the current length and the current position are filled +with null characters before the write is performed. +.Pp +After a successful call to +.Xr fclose 3 +or +.Xr fflush 3 , +the pointer referenced by +.Fa bufp +will contain the start of the memory buffer and the variable referenced by +.Fa sizep +will contain the smaller of the current position and the current buffer length. +.Pp +After a successful call to +.Xr fflush 3, +the pointer referenced by +.Fa bufp +and the variable referenced by +.Fa sizep +are only valid until the next write operation or a call to +.Xr fclose 3. +.Pp +Once a stream is closed, +the allocated buffer referenced by +.Fa bufp +should be released via a call to +.Xr free 3 +when it is no longer needed. +.Sh IMPLEMENTATION NOTES +Internally all I/O streams are effectively byte-oriented, +so using wide-oriented operations to write to a stream opened via +.Fn open_wmemstream +results in wide characters being expanded to a stream of multibyte characters +in stdio's internal buffers. +These multibyte characters are then converted back to wide characters when +written into the stream. +As a result, +the wide-oriented streams maintain an internal multibyte character conversion +state that is cleared on any seek opertion that changes the current position. +This should have no effect as long as wide-oriented output operations are used +on a wide-oriented stream. +.Sh RETURN VALUES +Upon successful completion, +.Fn open_memstream +and +.Fn open_wmemstream +return a +.Tn 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 bufp +or +.Fa sizep +argument was +.Dv NULL . +.It Bq Er ENOMEM +Memory for the stream or buffer could not be allocated. +.El +.Sh SEE ALSO +.Xr fclose 3 , +.Xr fflush 3 , +.Xr fopen 3 , +.Xr free 3 , +.Xr fseek 3 , +.Xr sbuf 3 , +.Xr stdio 3 +.Sh STANDARDS +The +.Fn open_memstream +and +.Fn open_wmemstream +functions conform to +.St -p1003.1-2008 . Index: src/lib/libc/stdio/open_memstream.c diff -u /dev/null src/lib/libc/stdio/open_memstream.c:1.1 --- /dev/null Sun Oct 12 20:40:36 2014 +++ src/lib/libc/stdio/open_memstream.c Sun Oct 12 20:40:36 2014 @@ -0,0 +1,214 @@ +/* $NetBSD: open_memstream.c,v 1.1 2014/10/13 00:40:36 christos Exp $ */ + +/*- + * Copyright (c) 2013 Advanced Computing Technologies LLC + * Written by: John H. Baldwin <j...@freebsd.org> + * 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 <sys/cdefs.h> +#if 0 +__FBSDID("$FreeBSD: head/lib/libc/stdio/open_memstream.c 247411 2013-02-27 19:50:46Z jhb $"); +#endif +__RCSID("$NetBSD: open_memstream.c,v 1.1 2014/10/13 00:40:36 christos Exp $"); + +#include "namespace.h" +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#define OFF_MAX LLONG_MAX + +struct memstream { + char **bufp; + size_t *sizep; + size_t len; + size_t offset; +}; + +static int +memstream_grow(struct memstream *ms, off_t newoff) +{ + char *buf; + size_t newsize; + + if (newoff < 0 || newoff >= SSIZE_MAX) + newsize = SSIZE_MAX - 1; + else + newsize = newoff; + if (newsize > ms->len) { + buf = realloc(*ms->bufp, newsize + 1); + if (buf != NULL) { +#ifdef DEBUG + fprintf(stderr, "MS: %p growing from %zd to %zd\n", + ms, ms->len, newsize); +#endif + memset(buf + ms->len + 1, 0, newsize - ms->len); + *ms->bufp = buf; + ms->len = newsize; + return (1); + } + return (0); + } + return (1); +} + +static void +memstream_update(struct memstream *ms) +{ + + *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset; +} + +static ssize_t +memstream_write(void *cookie, const void *buf, size_t len) +{ + struct memstream *ms; + size_t tocopy; + off_t more; + + ms = cookie; + more = ms->offset; + more += len; + if (!memstream_grow(ms, more)) + return (-1); + tocopy = ms->len - ms->offset; + if (len < tocopy) + tocopy = len; + memcpy(*ms->bufp + ms->offset, buf, tocopy); + ms->offset += tocopy; + memstream_update(ms); +#ifdef DEBUG + fprintf(stderr, "MS: write(%p, %zu) = %zu\n", ms, len, tocopy); +#endif + return (ssize_t)tocopy; +} + +static off_t +memstream_seek(void *cookie, off_t pos, int whence) +{ + struct memstream *ms; +#ifdef DEBUG + size_t old; +#endif + + ms = cookie; +#ifdef DEBUG + old = ms->offset; +#endif + switch (whence) { + case SEEK_SET: + /* _fseeko() checks for negative offsets. */ + assert(pos >= 0); + ms->offset = pos; + break; + case SEEK_CUR: + /* This is only called by _ftello(). */ + assert(pos == 0); + break; + case SEEK_END: + if (pos < 0) { + if (pos + (ssize_t)ms->len < 0) { +#ifdef DEBUG + fprintf(stderr, + "MS: bad SEEK_END: pos %jd, len %zu\n", + (intmax_t)pos, ms->len); +#endif + errno = EINVAL; + return (-1); + } + } else { + if (OFF_MAX - (ssize_t)ms->len < pos) { +#ifdef DEBUG + fprintf(stderr, + "MS: bad SEEK_END: pos %jd, len %zu\n", + (intmax_t)pos, ms->len); +#endif + errno = EOVERFLOW; + return (-1); + } + } + ms->offset = ms->len + pos; + break; + } + memstream_update(ms); +#ifdef DEBUG + fprintf(stderr, "MS: seek(%p, %jd, %d) %zu -> %zu\n", ms, + (intmax_t)pos, whence, old, ms->offset); +#endif + return (ms->offset); +} + +static int +memstream_close(void *cookie) +{ + + free(cookie); + return (0); +} + +FILE * +open_memstream(char **bufp, size_t *sizep) +{ + struct memstream *ms; + int save_errno; + FILE *fp; + + if (bufp == NULL || sizep == NULL) { + errno = EINVAL; + return (NULL); + } + *bufp = calloc(1, 1); + if (*bufp == NULL) + return (NULL); + ms = malloc(sizeof(*ms)); + if (ms == NULL) { + save_errno = errno; + free(*bufp); + *bufp = NULL; + errno = save_errno; + return (NULL); + } + ms->bufp = bufp; + ms->sizep = sizep; + ms->len = 0; + ms->offset = 0; + memstream_update(ms); + fp = funopen2(ms, NULL, memstream_write, memstream_seek, + NULL, memstream_close); + if (fp == NULL) { + save_errno = errno; + free(ms); + free(*bufp); + *bufp = NULL; + errno = save_errno; + return (NULL); + } + fwide(fp, -1); + return (fp); +} Index: src/lib/libc/stdio/open_wmemstream.c diff -u /dev/null src/lib/libc/stdio/open_wmemstream.c:1.1 --- /dev/null Sun Oct 12 20:40:36 2014 +++ src/lib/libc/stdio/open_wmemstream.c Sun Oct 12 20:40:36 2014 @@ -0,0 +1,273 @@ +/* $NetBSD: open_wmemstream.c,v 1.1 2014/10/13 00:40:36 christos Exp $ */ + +/*- + * Copyright (c) 2013 Advanced Computing Technologies LLC + * Written by: John H. Baldwin <j...@freebsd.org> + * 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 <sys/cdefs.h> +#if 0 +__FBSDID("$FreeBSD: head/lib/libc/stdio/open_wmemstream.c 247411 2013-02-27 19:50:46Z jhb $"); +#endif +__RCSID("$NetBSD: open_wmemstream.c,v 1.1 2014/10/13 00:40:36 christos Exp $"); + +#include "namespace.h" +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#define OFF_MAX LLONG_MAX + +struct wmemstream { + wchar_t **bufp; + size_t *sizep; + size_t len; + size_t offset; + mbstate_t mbstate; +}; + +static int +wmemstream_grow(struct wmemstream *ms, size_t newoff) +{ + wchar_t *buf; + size_t newsize; + + if (newoff >= (off_t)(SSIZE_MAX / sizeof(wchar_t))) + newsize = SSIZE_MAX / sizeof(wchar_t) - 1; + else + newsize = newoff; + if (newsize > ms->len) { + buf = realloc(*ms->bufp, (newsize + 1) * sizeof(wchar_t)); + if (buf != NULL) { +#ifdef DEBUG + fprintf(stderr, "WMS: %p growing from %zu to %zu\n", + ms, ms->len, newsize); +#endif + wmemset(buf + ms->len + 1, 0, newsize - ms->len); + *ms->bufp = buf; + ms->len = newsize; + return (1); + } + return (0); + } + return (1); +} + +static void +wmemstream_update(struct wmemstream *ms) +{ + + *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset; +} + +/* + * Based on a starting multibyte state and an input buffer, determine + * how many wchar_t's would be output. This doesn't use mbsnrtowcs() + * so that it can handle embedded null characters. + */ +static ssize_t +wbuflen(const mbstate_t *state, const char *buf, size_t len) +{ + mbstate_t lenstate; + size_t charlen, count; + + count = 0; + lenstate = *state; + while (len > 0) { + charlen = mbrlen(buf, len, &lenstate); + if (charlen == (size_t)-1) + return (-1); + if (charlen == (size_t)-2) + break; + if (charlen == 0) + /* XXX: Not sure how else to handle this. */ + charlen = 1; + len -= charlen; + buf += charlen; + count++; + } + return (count); +} + +static ssize_t +wmemstream_write(void *cookie, const void *buf, size_t len) +{ + struct wmemstream *ms; + ssize_t consumed, wlen; + size_t charlen; + + ms = cookie; + wlen = wbuflen(&ms->mbstate, buf, len); + if (wlen < 0) { + errno = EILSEQ; + return (-1); + } + if (!wmemstream_grow(ms, ms->offset + wlen)) + return (-1); + + /* + * This copies characters one at a time rather than using + * mbsnrtowcs() so it can properly handle embedded null + * characters. + */ + consumed = 0; + while (len > 0 && ms->offset < ms->len) { + charlen = mbrtowc(*ms->bufp + ms->offset, buf, len, + &ms->mbstate); + if (charlen == (size_t)-1) { + if (consumed == 0) { + errno = EILSEQ; + return (-1); + } + /* Treat it as a successful short write. */ + break; + } + if (charlen == 0) + /* XXX: Not sure how else to handle this. */ + charlen = 1; + if (charlen == (size_t)-2) { + consumed += len; + len = 0; + } else { + consumed += charlen; + buf = (const char *)buf + charlen; + len -= charlen; + ms->offset++; + } + } + wmemstream_update(ms); +#ifdef DEBUG + fprintf(stderr, "WMS: write(%p, %zu) = %zd\n", ms, len, consumed); +#endif + return (consumed); +} + +static off_t +wmemstream_seek(void *cookie, off_t pos, int whence) +{ + struct wmemstream *ms; + size_t old; + + ms = cookie; + old = ms->offset; + switch (whence) { + case SEEK_SET: + /* _fseeko() checks for negative offsets. */ + assert(pos >= 0); + ms->offset = pos; + break; + case SEEK_CUR: + /* This is only called by _ftello(). */ + assert(pos == 0); + break; + case SEEK_END: + if (pos < 0) { + if (pos + (ssize_t)ms->len < 0) { +#ifdef DEBUG + fprintf(stderr, + "WMS: bad SEEK_END: pos %jd, len %zd\n", + (intmax_t)pos, ms->len); +#endif + errno = EINVAL; + return (-1); + } + } else { + if (OFF_MAX - ms->len < (size_t)pos) { +#ifdef DEBUG + fprintf(stderr, + "WMS: bad SEEK_END: pos %jd, len %zd\n", + (intmax_t)pos, ms->len); +#endif + errno = EOVERFLOW; + return (-1); + } + } + ms->offset = ms->len + pos; + break; + } + /* Reset the multibyte state if a seek changes the position. */ + if (ms->offset != old) + memset(&ms->mbstate, 0, sizeof(ms->mbstate)); + wmemstream_update(ms); +#ifdef DEBUG + fprintf(stderr, "WMS: seek(%p, %jd, %d) %jd -> %jd\n", ms, + (intmax_t)pos, whence, (intmax_t)old, (intmax_t)ms->offset); +#endif + return (ms->offset); +} + +static int +wmemstream_close(void *cookie) +{ + + free(cookie); + return (0); +} + +FILE * +open_wmemstream(wchar_t **bufp, size_t *sizep) +{ + struct wmemstream *ms; + int save_errno; + FILE *fp; + + if (bufp == NULL || sizep == NULL) { + errno = EINVAL; + return (NULL); + } + *bufp = calloc(1, sizeof(wchar_t)); + if (*bufp == NULL) + return (NULL); + ms = malloc(sizeof(*ms)); + if (ms == NULL) { + save_errno = errno; + free(*bufp); + *bufp = NULL; + errno = save_errno; + return (NULL); + } + ms->bufp = bufp; + ms->sizep = sizep; + ms->len = 0; + ms->offset = 0; + memset(&ms->mbstate, 0, sizeof(mbstate_t)); + wmemstream_update(ms); + fp = funopen2(ms, NULL, wmemstream_write, wmemstream_seek, + NULL, wmemstream_close); + if (fp == NULL) { + save_errno = errno; + free(ms); + free(*bufp); + *bufp = NULL; + errno = save_errno; + return (NULL); + } + fwide(fp, 1); + return (fp); +}