Date: Fri, 28 Jun 2013 16:07:21 +0000 (UTC) From: John Baldwin <jhb@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org Subject: svn commit: r252343 - in stable/9: include lib/libc/stdio tools/regression/lib/libc/stdio Message-ID: <201306281607.r5SG7L3e009715@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jhb Date: Fri Jun 28 16:07:20 2013 New Revision: 252343 URL: http://svnweb.freebsd.org/changeset/base/252343 Log: MFC 246120,246148,246206,246587,247411,247415: Add fmemopen(3), open_memstream(3), and open_wmemstream(3) which provide stdio FILE objects for memory buffers. port exprun by: bdrewery Added: stable/9/lib/libc/stdio/fmemopen.c - copied, changed from r246120, head/lib/libc/stdio/fmemopen.c stable/9/lib/libc/stdio/open_memstream.3 - copied, changed from r247411, head/lib/libc/stdio/open_memstream.3 stable/9/lib/libc/stdio/open_memstream.c - copied unchanged from r247411, head/lib/libc/stdio/open_memstream.c stable/9/lib/libc/stdio/open_wmemstream.c - copied unchanged from r247411, head/lib/libc/stdio/open_wmemstream.c stable/9/tools/regression/lib/libc/stdio/test-fmemopen.c - copied, changed from r246120, head/tools/regression/lib/libc/stdio/test-fmemopen.c stable/9/tools/regression/lib/libc/stdio/test-fmemopen.t - copied unchanged from r246120, head/tools/regression/lib/libc/stdio/test-fmemopen.t stable/9/tools/regression/lib/libc/stdio/test-open_memstream.c - copied unchanged from r247411, head/tools/regression/lib/libc/stdio/test-open_memstream.c stable/9/tools/regression/lib/libc/stdio/test-open_memstream.t - copied unchanged from r247411, head/tools/regression/lib/libc/stdio/test-open_memstream.t stable/9/tools/regression/lib/libc/stdio/test-open_wmemstream.c - copied unchanged from r247411, head/tools/regression/lib/libc/stdio/test-open_wmemstream.c stable/9/tools/regression/lib/libc/stdio/test-open_wmemstream.t - copied unchanged from r247411, head/tools/regression/lib/libc/stdio/test-open_wmemstream.t Modified: stable/9/include/stdio.h (contents, props changed) stable/9/include/wchar.h stable/9/lib/libc/stdio/Makefile.inc (contents, props changed) stable/9/lib/libc/stdio/Symbol.map (contents, props changed) stable/9/lib/libc/stdio/fopen.3 (contents, props changed) stable/9/tools/regression/lib/libc/stdio/Makefile Directory Properties: stable/9/include/ (props changed) stable/9/lib/libc/ (props changed) stable/9/tools/regression/lib/libc/ (props changed) Modified: stable/9/include/stdio.h ============================================================================== --- stable/9/include/stdio.h Fri Jun 28 15:55:30 2013 (r252342) +++ stable/9/include/stdio.h Fri Jun 28 16:07:20 2013 (r252343) @@ -343,8 +343,10 @@ char *tempnam(const char *, const char * #endif #if __BSD_VISIBLE || __POSIX_VISIBLE >= 200809 +FILE *fmemopen(void * __restrict, size_t, const char * __restrict); ssize_t getdelim(char ** __restrict, size_t * __restrict, int, FILE * __restrict); +FILE *open_memstream(char **, size_t *); int renameat(int, const char *, int, const char *); int vdprintf(int, const char * __restrict, __va_list); Modified: stable/9/include/wchar.h ============================================================================== --- stable/9/include/wchar.h Fri Jun 28 15:55:30 2013 (r252342) +++ stable/9/include/wchar.h Fri Jun 28 16:07:20 2013 (r252343) @@ -209,6 +209,7 @@ int wcwidth(wchar_t); #if __POSIX_VISIBLE >= 200809 || __BSD_VISIBLE size_t mbsnrtowcs(wchar_t * __restrict, const char ** __restrict, size_t, size_t, mbstate_t * __restrict); +FILE *open_wmemstream(wchar_t **, size_t *); 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 *) __malloc_like; Modified: stable/9/lib/libc/stdio/Makefile.inc ============================================================================== --- stable/9/lib/libc/stdio/Makefile.inc Fri Jun 28 15:55:30 2013 (r252342) +++ stable/9/lib/libc/stdio/Makefile.inc Fri Jun 28 16:07:20 2013 (r252343) @@ -8,11 +8,13 @@ SRCS+= _flock_stub.c asprintf.c clrerr.c fclose.c fcloseall.c fdopen.c \ feof.c ferror.c fflush.c fgetc.c fgetln.c fgetpos.c fgets.c fgetwc.c \ fgetwln.c fgetws.c \ - fileno.c findfp.c flags.c fopen.c fprintf.c fpurge.c fputc.c fputs.c \ + fileno.c findfp.c flags.c fmemopen.c fopen.c fprintf.c fpurge.c \ + fputc.c fputs.c \ fputwc.c fputws.c fread.c freopen.c fscanf.c fseek.c fsetpos.c \ ftell.c funopen.c fvwrite.c fwalk.c fwide.c fwprintf.c fwscanf.c \ fwrite.c getc.c getchar.c getdelim.c getline.c \ gets.c getw.c getwc.c getwchar.c makebuf.c mktemp.c \ + open_memstream.c open_wmemstream.c \ perror.c printf.c printf-pos.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 \ @@ -35,7 +37,7 @@ MAN+= fclose.3 ferror.3 fflush.3 fgetln. flockfile.3 \ fopen.3 fputs.3 \ fputws.3 fread.3 fseek.3 funopen.3 fwide.3 getc.3 \ - getline.3 getwc.3 mktemp.3 \ + getline.3 getwc.3 mktemp.3 open_memstream.3 \ printf.3 printf_l.3 putc.3 putwc.3 remove.3 scanf.3 scanf_l.3 setbuf.3 \ stdio.3 tmpnam.3 \ ungetc.3 ungetwc.3 wprintf.3 wscanf.3 @@ -48,7 +50,7 @@ MLINKS+=ferror.3 ferror_unlocked.3 \ MLINKS+=fflush.3 fpurge.3 MLINKS+=fgets.3 gets.3 MLINKS+=flockfile.3 ftrylockfile.3 flockfile.3 funlockfile.3 -MLINKS+=fopen.3 fdopen.3 fopen.3 freopen.3 +MLINKS+=fopen.3 fdopen.3 fopen.3 freopen.3 fopen.3 fmemopen.3 MLINKS+=fputs.3 puts.3 MLINKS+=fread.3 fwrite.3 MLINKS+=fseek.3 fgetpos.3 fseek.3 fseeko.3 fseek.3 fsetpos.3 fseek.3 ftell.3 \ @@ -59,6 +61,7 @@ MLINKS+=getc.3 fgetc.3 getc.3 getc_unloc MLINKS+=getline.3 getdelim.3 MLINKS+=getwc.3 fgetwc.3 getwc.3 getwchar.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 \ Modified: stable/9/lib/libc/stdio/Symbol.map ============================================================================== --- stable/9/lib/libc/stdio/Symbol.map Fri Jun 28 15:55:30 2013 (r252342) +++ stable/9/lib/libc/stdio/Symbol.map Fri Jun 28 16:07:20 2013 (r252343) @@ -155,6 +155,9 @@ FBSD_1.3 { getwchar_l; putwc_l; putwchar_l; + fmemopen; + open_memstream; + open_wmemstream; }; FBSDprivate_1.0 { Copied and modified: stable/9/lib/libc/stdio/fmemopen.c (from r246120, head/lib/libc/stdio/fmemopen.c) ============================================================================== --- head/lib/libc/stdio/fmemopen.c Wed Jan 30 14:59:26 2013 (r246120, copy source) +++ stable/9/lib/libc/stdio/fmemopen.c Fri Jun 28 16:07:20 2013 (r252343) @@ -1,99 +1,166 @@ /*- -Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org> - -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 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 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. -*/ + * Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org> + * + * 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 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 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> __FBSDID("$FreeBSD$"); +#include <fcntl.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> +#include "local.h" -struct __fmemopen_cookie +struct fmemopen_cookie { - char *buf; /* pointer to the memory region */ - char own; /* did we allocate the buffer ourselves? */ - long len; /* buffer length in bytes */ - long off; /* current offset into the buffer */ + char *buf; /* pointer to the memory region */ + bool own; /* did we allocate the buffer ourselves? */ + char bin; /* is this a binary buffer? */ + size_t size; /* buffer length in bytes */ + size_t len; /* data length in bytes */ + size_t off; /* current offset into the buffer */ }; -static int fmemopen_read (void *cookie, char *buf, int nbytes); -static int fmemopen_write (void *cookie, const char *buf, int nbytes); -static fpos_t fmemopen_seek (void *cookie, fpos_t offset, int whence); -static int fmemopen_close (void *cookie); +static int fmemopen_read(void *cookie, char *buf, int nbytes); +static int fmemopen_write(void *cookie, const char *buf, int nbytes); +static fpos_t fmemopen_seek(void *cookie, fpos_t offset, int whence); +static int fmemopen_close(void *cookie); FILE * -fmemopen (void * __restrict buf, size_t size, const char * __restrict mode) +fmemopen(void * __restrict buf, size_t size, const char * __restrict mode) { - /* allocate cookie */ - struct __fmemopen_cookie *ck = malloc (sizeof (struct __fmemopen_cookie)); + struct fmemopen_cookie *ck; + FILE *f; + int flags, rc; + + /* + * Retrieve the flags as used by open(2) from the mode argument, and + * validate them. + */ + rc = __sflags(mode, &flags); + if (rc == 0) { + errno = EINVAL; + return (NULL); + } + + /* + * There's no point in requiring an automatically allocated buffer + * in write-only mode. + */ + if (!(flags & O_RDWR) && buf == NULL) { + errno = EINVAL; + return (NULL); + } + + ck = malloc(sizeof(struct fmemopen_cookie)); if (ck == NULL) { - errno = ENOMEM; return (NULL); } - ck->off = 0; - ck->len = size; + ck->off = 0; + ck->size = size; - /* do we have to allocate the buffer ourselves? */ + /* Check whether we have to allocate the buffer ourselves. */ ck->own = ((ck->buf = buf) == NULL); if (ck->own) { - ck->buf = malloc (size); + ck->buf = malloc(size); if (ck->buf == NULL) { - free (ck); - errno = ENOMEM; + free(ck); return (NULL); } + } + + /* + * POSIX distinguishes between w+ and r+, in that w+ is supposed to + * truncate the buffer. + */ + if (ck->own || mode[0] == 'w') { ck->buf[0] = '\0'; } - if (mode[0] == 'a') - ck->off = strnlen(ck->buf, ck->len); + /* Check for binary mode. */ + ck->bin = strchr(mode, 'b') != NULL; - /* actuall wrapper */ - FILE *f = funopen ((void *)ck, fmemopen_read, fmemopen_write, + /* + * The size of the current buffer contents is set depending on the + * mode: + * + * for append (text-mode), the position of the first NULL byte, or the + * size of the buffer if none is found + * + * for append (binary-mode), the size of the buffer + * + * for read, the size of the buffer + * + * for write, 0 + */ + switch (mode[0]) { + case 'a': + if (ck->bin) { + /* + * This isn't useful, since the buffer isn't allowed + * to grow. + */ + ck->off = ck->len = size; + } else + ck->off = ck->len = strnlen(ck->buf, ck->size); + break; + case 'r': + ck->len = size; + break; + case 'w': + ck->len = 0; + break; + } + + f = funopen(ck, + flags & O_WRONLY ? NULL : fmemopen_read, + flags & O_RDONLY ? NULL : fmemopen_write, fmemopen_seek, fmemopen_close); if (f == NULL) { if (ck->own) - free (ck->buf); - free (ck); + free(ck->buf); + free(ck); return (NULL); } - /* turn off buffering, so a write past the end of the buffer - * correctly returns a short object count */ - setvbuf (f, (char *) NULL, _IONBF, 0); + /* + * Turn off buffering, so a write past the end of the buffer + * correctly returns a short object count. + */ + setvbuf(f, NULL, _IONBF, 0); return (f); } static int -fmemopen_read (void *cookie, char *buf, int nbytes) +fmemopen_read(void *cookie, char *buf, int nbytes) { - struct __fmemopen_cookie *ck = cookie; + struct fmemopen_cookie *ck = cookie; if (nbytes > ck->len - ck->off) nbytes = ck->len - ck->off; @@ -101,7 +168,7 @@ fmemopen_read (void *cookie, char *buf, if (nbytes == 0) return (0); - memcpy (buf, ck->buf + ck->off, nbytes); + memcpy(buf, ck->buf + ck->off, nbytes); ck->off += nbytes; @@ -109,35 +176,44 @@ fmemopen_read (void *cookie, char *buf, } static int -fmemopen_write (void *cookie, const char *buf, int nbytes) +fmemopen_write(void *cookie, const char *buf, int nbytes) { - struct __fmemopen_cookie *ck = cookie; + struct fmemopen_cookie *ck = cookie; - if (nbytes > ck->len - ck->off) - nbytes = ck->len - ck->off; + if (nbytes > ck->size - ck->off) + nbytes = ck->size - ck->off; if (nbytes == 0) return (0); - memcpy (ck->buf + ck->off, buf, nbytes); + memcpy(ck->buf + ck->off, buf, nbytes); ck->off += nbytes; - if (ck->off < ck->len && ck->buf[ck->off - 1] != '\0') + if (ck->off > ck->len) + ck->len = ck->off; + + /* + * We append a NULL byte if all these conditions are met: + * - the buffer is not binary + * - the buffer is not full + * - the data just written doesn't already end with a NULL byte + */ + if (!ck->bin && ck->off < ck->size && ck->buf[ck->off - 1] != '\0') ck->buf[ck->off] = '\0'; return (nbytes); } static fpos_t -fmemopen_seek (void *cookie, fpos_t offset, int whence) +fmemopen_seek(void *cookie, fpos_t offset, int whence) { - struct __fmemopen_cookie *ck = cookie; + struct fmemopen_cookie *ck = cookie; switch (whence) { case SEEK_SET: - if (offset > ck->len) { + if (offset > ck->size) { errno = EINVAL; return (-1); } @@ -145,7 +221,7 @@ fmemopen_seek (void *cookie, fpos_t offs break; case SEEK_CUR: - if (ck->off + offset > ck->len) { + if (ck->off + offset > ck->size) { errno = EINVAL; return (-1); } @@ -169,14 +245,14 @@ fmemopen_seek (void *cookie, fpos_t offs } static int -fmemopen_close (void *cookie) +fmemopen_close(void *cookie) { - struct __fmemopen_cookie *ck = cookie; + struct fmemopen_cookie *ck = cookie; if (ck->own) - free (ck->buf); + free(ck->buf); - free (ck); + free(ck); return (0); } Modified: stable/9/lib/libc/stdio/fopen.3 ============================================================================== --- stable/9/lib/libc/stdio/fopen.3 Fri Jun 28 15:55:30 2013 (r252342) +++ stable/9/lib/libc/stdio/fopen.3 Fri Jun 28 16:07:20 2013 (r252343) @@ -32,13 +32,14 @@ .\" @(#)fopen.3 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd October 17, 2011 +.Dd January 30, 2013 .Dt FOPEN 3 .Os .Sh NAME .Nm fopen , .Nm fdopen , -.Nm freopen +.Nm freopen , +.Nm fmemopen .Nd stream open functions .Sh LIBRARY .Lb libc @@ -50,6 +51,8 @@ .Fn fdopen "int fildes" "const char *mode" .Ft FILE * .Fn freopen "const char *path" "const char *mode" "FILE *stream" +.Ft FILE * +.Fn fmemopen "void *restrict *buf" "size_t size" "const char * restrict mode" .Sh DESCRIPTION The .Fn fopen @@ -107,7 +110,9 @@ after either the or the first letter. This is strictly for compatibility with .St -isoC -and has no effect; the ``b'' is ignored. +and has effect only for +.Fn fmemopen +; otherwise the ``b'' is ignored. .Pp Any created files will have mode .Do Dv S_IRUSR @@ -189,6 +194,34 @@ standard text stream .Dv ( stderr , stdin , or .Dv stdout ) . +.Pp +The +.Fn fmemopen +function +associates the buffer given by the +.Fa buf +and +.Fa size +arguments with a stream. +The +.Fa buf +argument is either a null pointer or point to a buffer that +is at least +.Fa size +bytes long. +If a null pointer is specified as the +.Fa buf +argument, +.Fn fmemopen +allocates +.Fa size +bytes of memory. This buffer is automatically freed when the +stream is closed. Buffers can be opened in text-mode (default) or binary-mode +(if ``b'' is present in the second or third position of the +.Fa mode +argument). Buffers opened in text-mode make sure that writes are terminated with +a NULL byte, if the last write hasn't filled up the whole buffer. Buffers +opened in binary-mode never append a NULL byte. .Sh RETURN VALUES Upon successful completion .Fn fopen , @@ -212,16 +245,18 @@ argument to .Fn fopen , .Fn fdopen , +.Fn freopen , or -.Fn freopen +.Fn fmemopen was invalid. .El .Pp The .Fn fopen , -.Fn fdopen -and +.Fn fdopen , .Fn freopen +and +.Fn fmemopen functions may also fail and set .Va errno @@ -277,3 +312,10 @@ The function conforms to .St -p1003.1-88 . +The +.Fn fmemopen +function +conforms to +.St -p1003.1-2008 . +The ``b'' mode does not conform to any standard +but is also supported by glibc. Copied and modified: stable/9/lib/libc/stdio/open_memstream.3 (from r247411, head/lib/libc/stdio/open_memstream.3) ============================================================================== --- head/lib/libc/stdio/open_memstream.3 Wed Feb 27 19:50:46 2013 (r247411, copy source) +++ stable/9/lib/libc/stdio/open_memstream.3 Fri Jun 28 16:07:20 2013 (r252343) @@ -137,6 +137,7 @@ 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 , Copied: stable/9/lib/libc/stdio/open_memstream.c (from r247411, head/lib/libc/stdio/open_memstream.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/9/lib/libc/stdio/open_memstream.c Fri Jun 28 16:07:20 2013 (r252343, copy of r247411, head/lib/libc/stdio/open_memstream.c) @@ -0,0 +1,209 @@ +/*- + * Copyright (c) 2013 Advanced Computing Technologies LLC + * Written by: John H. Baldwin <jhb@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> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#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 int +memstream_grow(struct memstream *ms, fpos_t newoff) +{ + 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); +} + +static void +memstream_update(struct memstream *ms) +{ + + assert(ms->len >= 0 && ms->offset >= 0); + *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset; +} + +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_seek(void *cookie, fpos_t pos, int whence) +{ + 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: + /* 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; + } + 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_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 = funopen(ms, NULL, memstream_write, memstream_seek, + memstream_close); + if (fp == NULL) { + save_errno = errno; + free(ms); + free(*bufp); + *bufp = NULL; + errno = save_errno; + return (NULL); + } + fwide(fp, -1); + return (fp); +} Copied: stable/9/lib/libc/stdio/open_wmemstream.c (from r247411, head/lib/libc/stdio/open_wmemstream.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/9/lib/libc/stdio/open_wmemstream.c Fri Jun 28 16:07:20 2013 (r252343, copy of r247411, head/lib/libc/stdio/open_wmemstream.c) @@ -0,0 +1,271 @@ +/*- + * Copyright (c) 2013 Advanced Computing Technologies LLC + * Written by: John H. Baldwin <jhb@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> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#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); +} Modified: stable/9/tools/regression/lib/libc/stdio/Makefile ============================================================================== --- stable/9/tools/regression/lib/libc/stdio/Makefile Fri Jun 28 15:55:30 2013 (r252342) +++ stable/9/tools/regression/lib/libc/stdio/Makefile Fri Jun 28 16:07:20 2013 (r252343) @@ -1,6 +1,8 @@ # $FreeBSD$ -TESTS= test-getdelim test-perror test-print-positional test-printbasic test-printfloat test-scanfloat *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201306281607.r5SG7L3e009715>