Skip site navigation (1)Skip section navigation (2)
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>