From 2c8c2922834904924d7b42db7bd943aaa20f3452 Mon Sep 17 00:00:00 2001 From: Sascha Wildner Date: Sat, 5 Sep 2015 20:48:49 +0200 Subject: [PATCH] libc: Sync open_memstream(3) with FreeBSD. Also add open_wmemstream(3). Their open_memstream(3) implementation is more compliant than ours was. Taken-from: FreeBSD --- include/wchar.h | 2 - lib/libc/stdio/Makefile.inc | 6 +- lib/libc/stdio/Symbol.map | 1 + lib/libc/stdio/open_memstream.3 | 142 ++++++++++++---- lib/libc/stdio/open_memstream.c | 240 ++++++++++++++++----------- lib/libc/stdio/open_wmemstream.c | 270 +++++++++++++++++++++++++++++++ 6 files changed, 528 insertions(+), 133 deletions(-) create mode 100644 lib/libc/stdio/open_wmemstream.c diff --git a/include/wchar.h b/include/wchar.h index b3e03b608d..70ba47ad92 100644 --- a/include/wchar.h +++ b/include/wchar.h @@ -222,9 +222,7 @@ int wcwidth(wchar_t); #if __POSIX_VISIBLE >= 200809 size_t mbsnrtowcs(wchar_t * __restrict, const char ** __restrict, size_t, size_t, mbstate_t * __restrict); -#if 0 /* not implemented yet */ FILE *open_wmemstream(wchar_t **, size_t *); -#endif wchar_t *wcpcpy(wchar_t * __restrict, const wchar_t * __restrict); wchar_t *wcpncpy(wchar_t * __restrict, const wchar_t * __restrict, size_t); wchar_t *wcsdup(const wchar_t *); diff --git a/lib/libc/stdio/Makefile.inc b/lib/libc/stdio/Makefile.inc index a349b86838..4ba85b28a3 100644 --- a/lib/libc/stdio/Makefile.inc +++ b/lib/libc/stdio/Makefile.inc @@ -16,8 +16,9 @@ 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 open_memstream.c perror.c \ - printf-pos.c printf.c putc.c \ + getwc.c getwchar.c makebuf.c mktemp.c \ + open_memstream.c open_wmemstream.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 \ sreadahead.c \ @@ -62,6 +63,7 @@ MLINKS+=getline.3 getdelim.3 MLINKS+=getwc.3 fgetwc.3 getwc.3 fgetwc_l.3 getwc.3 getwc_l.3 \ getwc.3 getwchar.3 getwc.3 getwchar_l.3 MLINKS+=mktemp.3 mkdtemp.3 mktemp.3 mkstemp.3 mktemp.3 mkstemps.3 +MLINKS+=open_memstream.3 open_wmemstream.3 MLINKS+=printf.3 asprintf.3 printf.3 dprintf.3 printf.3 fprintf.3 \ printf.3 snprintf.3 printf.3 sprintf.3 \ printf.3 vasprintf.3 printf.3 vdprintf.3 \ diff --git a/lib/libc/stdio/Symbol.map b/lib/libc/stdio/Symbol.map index d9a1f85cce..a7d5128e6f 100644 --- a/lib/libc/stdio/Symbol.map +++ b/lib/libc/stdio/Symbol.map @@ -80,6 +80,7 @@ DF404.0 { mkstemps; mktemp; open_memstream; + open_wmemstream; perror; printf; printf_l; diff --git a/lib/libc/stdio/open_memstream.3 b/lib/libc/stdio/open_memstream.3 index 43a109e3a8..e42b3351be 100644 --- a/lib/libc/stdio/open_memstream.3 +++ b/lib/libc/stdio/open_memstream.3 @@ -1,5 +1,5 @@ -.\" -.\" Copyright (c) 2011 Venkatesh Srinivas, +.\" Copyright (c) 2013 Hudson River Trading LLC +.\" Written by: John H. Baldwin .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -11,54 +11,115 @@ .\" 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 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. +.\" 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 286177 2015-08-02 02:00:20Z jhb $ .\" -.Dd September 12, 2011 +.Dd September 9, 2015 .Dt OPEN_MEMSTREAM 3 .Os .Sh NAME -.Nm open_memstream -.Nd open a stream that points to a dynamic buffer +.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 -function creates a -.Vt FILE -stream with a dynamically expanding buffer. -A pointer to the buffer is stored at the +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 -argument and the length of the data in the buffer is stored at the +will contain the start of the memory buffer and the variable referenced by .Fa sizep -argument. -The pointer and length values may be updated by writes to the buffer. +will contain the smaller of the current position and the current buffer length. .Pp -Writes append to the buffer and may reallocate it, invalidating the +After a successful call to +.Xr fflush 3 , +the pointer referenced by .Fa bufp -argument. -Writes may fail if the buffer cannot be resized. +and the variable referenced by +.Fa sizep +are only valid until the next write operation or a call to +.Xr fclose 3 . .Pp -The stream is seekable and opened for writing. +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 -returns a -.Vt FILE +and +.Fn open_wmemstream +return a +.Tn FILE pointer. Otherwise, .Dv NULL @@ -70,20 +131,35 @@ is set to indicate the error. .It Bq Er EINVAL The .Fa bufp -argument or the +or .Fa sizep -argument were +argument was .Dv NULL . .It Bq Er ENOMEM -Memory for the stream buffer or cookie could not be allocated. +Memory for the stream or buffer could not be allocated. .El .Sh SEE ALSO .Xr fclose 3 , .Xr fflush 3 , .Xr fmemopen 3 , -.Xr fopen 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 . .Sh HISTORY The .Fn open_memstream function first appeared in .Dx 2.11 . +The +.Fn open_wmemstream +function first appeared in +.Dx 4.3 . diff --git a/lib/libc/stdio/open_memstream.c b/lib/libc/stdio/open_memstream.c index a2a619c772..9e49afb18b 100644 --- a/lib/libc/stdio/open_memstream.c +++ b/lib/libc/stdio/open_memstream.c @@ -1,5 +1,6 @@ /*- - * Copyright (c) 2011 Venkatesh Srinivas, + * Copyright (c) 2013 Hudson River Trading LLC + * Written by: John H. Baldwin * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,139 +23,186 @@ * 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.c 281887 2015-04-23 14:22:20Z jhb $ */ +#include "namespace.h" +#include +#include +#include #include #include -#include -#include - -struct memstream_cookie { - char **pub_buf; - size_t *pub_size; - - char *head; - size_t pos; - size_t tail; +#include +#include +#include "un-namespace.h" + +/* XXX: There is no FPOS_MAX. This assumes fpos_t is an off_t. */ +#define FPOS_MAX OFF_MAX + +struct memstream { + char **bufp; + size_t *sizep; + ssize_t len; + fpos_t offset; }; -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) +memstream_grow(struct memstream *ms, fpos_t newoff) { - 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; + char *buf; + ssize_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); +} - bcopy(buf, &c->head[c->pos], len); - - c->tail = c->pos + len; - c->pos = c->tail; - c->head[c->tail] = '\0'; +static void +memstream_update(struct memstream *ms) +{ - sync_pub_cookie(c); + assert(ms->len >= 0 && ms->offset >= 0); + *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset; +} - return (len); +static int +memstream_write(void *cookie, const char *buf, int len) +{ + struct memstream *ms; + ssize_t tocopy; + + ms = cookie; + if (!memstream_grow(ms, ms->offset + len)) + 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, %d) = %zd\n", ms, len, tocopy); +#endif + return (tocopy); } static fpos_t -memstream_seekfn(void *cookie, fpos_t pos, int whence) +memstream_seek(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); + struct memstream *ms; +#ifdef DEBUG + fpos_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): - c->pos += pos; - return (c->pos); + case SEEK_CUR: + /* This is only called by _ftello(). */ + assert(pos == 0); + break; + case SEEK_END: + if (pos < 0) { + if (pos + ms->len < 0) { +#ifdef DEBUG + fprintf(stderr, + "MS: bad SEEK_END: pos %jd, len %zd\n", + (intmax_t)pos, ms->len); +#endif + errno = EINVAL; + return (-1); + } + } else { + if (FPOS_MAX - ms->len < pos) { +#ifdef DEBUG + fprintf(stderr, + "MS: bad SEEK_END: pos %jd, len %zd\n", + (intmax_t)pos, ms->len); +#endif + errno = EOVERFLOW; + return (-1); + } + } + ms->offset = ms->len + pos; break; - case (SEEK_END): - default: - errno = EINVAL; - return (fpos_t) -1; } + memstream_update(ms); +#ifdef DEBUG + fprintf(stderr, "MS: 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 -memstream_closefn(void *cookie) +memstream_close(void *cookie) { - struct memstream_cookie *c; - - c = cookie; - sync_pub_cookie(c); - - free(c); + free(cookie); return (0); } FILE * open_memstream(char **bufp, size_t *sizep) { + struct memstream *ms; + int save_errno; FILE *fp; - struct memstream_cookie *c; - fp = NULL; if (bufp == NULL || sizep == NULL) { errno = EINVAL; - goto out; + return (NULL); } - - c = malloc(sizeof(struct memstream_cookie)); - if (c == NULL) { - errno = EINVAL; - goto out; + *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); } - - fp = funopen(c, - NULL, - memstream_writefn, - memstream_seekfn, - memstream_closefn - ); - + ms->bufp = bufp; + ms->sizep = sizep; + ms->len = 0; + ms->offset = 0; + memstream_update(ms); + fp = funopen(ms, NULL, memstream_write, memstream_seek, + memstream_close); if (fp == NULL) { - free(c); - errno = ENOMEM; - goto out; + save_errno = errno; + free(ms); + free(*bufp); + *bufp = NULL; + errno = save_errno; + return (NULL); } - - c->pub_buf = bufp; - c->pub_size = sizep; - c->head = NULL; - c->tail = 0; - c->pos = 0; - -out: + fwide(fp, -1); return (fp); } diff --git a/lib/libc/stdio/open_wmemstream.c b/lib/libc/stdio/open_wmemstream.c new file mode 100644 index 0000000000..5adf779bff --- /dev/null +++ b/lib/libc/stdio/open_wmemstream.c @@ -0,0 +1,270 @@ +/*- + * Copyright (c) 2013 Hudson River Trading LLC + * Written by: John H. Baldwin + * 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_wmemstream.c 281887 2015-04-23 14:22:20Z jhb $ + */ + +#include "namespace.h" +#include +#include +#include +#include +#include +#include +#include +#include "un-namespace.h" + +/* XXX: There is no FPOS_MAX. This assumes fpos_t is an off_t. */ +#define FPOS_MAX OFF_MAX + +struct wmemstream { + wchar_t **bufp; + size_t *sizep; + ssize_t len; + fpos_t offset; + mbstate_t mbstate; +}; + +static int +wmemstream_grow(struct wmemstream *ms, fpos_t newoff) +{ + wchar_t *buf; + ssize_t newsize; + + if (newoff < 0 || newoff >= 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 %zd to %zd\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) +{ + + assert(ms->len >= 0 && ms->offset >= 0); + *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 size_t +wbuflen(const mbstate_t *state, const char *buf, int 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 int +wmemstream_write(void *cookie, const char *buf, int 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 += charlen; + len -= charlen; + ms->offset++; + } + } + wmemstream_update(ms); +#ifdef DEBUG + fprintf(stderr, "WMS: write(%p, %d) = %zd\n", ms, len, consumed); +#endif + return (consumed); +} + +static fpos_t +wmemstream_seek(void *cookie, fpos_t pos, int whence) +{ + struct wmemstream *ms; + fpos_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 + 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 (FPOS_MAX - ms->len < 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 = funopen(ms, NULL, wmemstream_write, wmemstream_seek, + wmemstream_close); + if (fp == NULL) { + save_errno = errno; + free(ms); + free(*bufp); + *bufp = NULL; + errno = save_errno; + return (NULL); + } + fwide(fp, 1); + return (fp); +} -- 2.41.0