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 <[email protected]>
-
-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 <[email protected]>
+ * 
+ * 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 <[email protected]>
+ * 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 <[email protected]>
+ * 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 ***
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-stable-9
To unsubscribe, send any mail to "[email protected]"

Reply via email to