OSDN Git Service

Add POSIX-2008 fmemopen, open_memstream, and open_wmemstream.
authorElliott Hughes <enh@google.com>
Wed, 20 Aug 2014 23:10:49 +0000 (16:10 -0700)
committerElliott Hughes <enh@google.com>
Thu, 21 Aug 2014 00:03:46 +0000 (17:03 -0700)
Bug: 17164505
Change-Id: I59e28a08ff8b6ab632230b11a5807cfd5278aeb5

libc/Android.mk
libc/include/stdio.h
libc/include/wchar.h
libc/upstream-openbsd/lib/libc/stdio/fmemopen.c [new file with mode: 0644]
libc/upstream-openbsd/lib/libc/stdio/open_memstream.c [new file with mode: 0644]
libc/upstream-openbsd/lib/libc/stdio/open_wmemstream.c [new file with mode: 0644]
tests/stdio_test.cpp
tests/wchar_test.cpp

index c268a87..487cbd8 100644 (file)
@@ -391,6 +391,7 @@ libc_upstream_openbsd_src_files := \
     upstream-openbsd/lib/libc/stdio/fgetws.c \
     upstream-openbsd/lib/libc/stdio/fileno.c \
     upstream-openbsd/lib/libc/stdio/findfp.c \
+    upstream-openbsd/lib/libc/stdio/fmemopen.c \
     upstream-openbsd/lib/libc/stdio/fprintf.c \
     upstream-openbsd/lib/libc/stdio/fpurge.c \
     upstream-openbsd/lib/libc/stdio/fputc.c \
@@ -419,6 +420,8 @@ libc_upstream_openbsd_src_files := \
     upstream-openbsd/lib/libc/stdio/getwchar.c \
     upstream-openbsd/lib/libc/stdio/makebuf.c \
     upstream-openbsd/lib/libc/stdio/mktemp.c \
+    upstream-openbsd/lib/libc/stdio/open_memstream.c \
+    upstream-openbsd/lib/libc/stdio/open_wmemstream.c \
     upstream-openbsd/lib/libc/stdio/perror.c \
     upstream-openbsd/lib/libc/stdio/printf.c \
     upstream-openbsd/lib/libc/stdio/putc.c \
index 8727a9f..ce60fd7 100644 (file)
@@ -325,6 +325,11 @@ int         putc_unlocked(int, FILE *);
 int     putchar_unlocked(int);
 #endif /* __POSIX_VISIBLE >= 199506 */
 
+#if __POSIX_VISIBLE >= 200809
+FILE* fmemopen(void*, size_t, const char*);
+FILE* open_memstream(char**, size_t*);
+#endif /* __POSIX_VISIBLE >= 200809 */
+
 __END_DECLS
 
 #endif /* __BSD_VISIBLE || __POSIX_VISIBLE || __XPG_VISIBLE */
index 1898c7e..ae10d93 100644 (file)
@@ -166,6 +166,7 @@ extern wint_t towctrans(wint_t, wctrans_t);
 extern wctrans_t wctrans(const char*);
 
 #if __POSIX_VISIBLE >= 200809
+FILE* open_wmemstream(wchar_t**, size_t*);
 wchar_t* wcsdup(const wchar_t*);
 size_t wcsnlen(const wchar_t*, size_t);
 #endif
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fmemopen.c b/libc/upstream-openbsd/lib/libc/stdio/fmemopen.c
new file mode 100644 (file)
index 0000000..8cda047
--- /dev/null
@@ -0,0 +1,183 @@
+/*     $OpenBSD: fmemopen.c,v 1.2 2013/03/27 15:06:25 mpi Exp $        */
+
+/*
+ * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
+ * Copyright (c) 2009 Ted Unangst
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "local.h"
+
+struct state {
+       char            *string;        /* actual stream */
+       size_t           pos;           /* current position */
+       size_t           size;          /* allocated size */
+       size_t           len;           /* length of the data */
+       int              update;        /* open for update */
+};
+
+static int
+fmemopen_read(void *v, char *b, int l)
+{
+       struct state    *st = v;
+       int              i;
+
+       for (i = 0; i < l && i + st->pos < st->len; i++)
+               b[i] = st->string[st->pos + i];
+       st->pos += i;
+
+       return (i);
+}
+
+static int
+fmemopen_write(void *v, const char *b, int l)
+{
+       struct state    *st = v;
+       int             i;
+
+       for (i = 0; i < l && i + st->pos < st->size; i++)
+               st->string[st->pos + i] = b[i];
+       st->pos += i;
+
+       if (st->pos >= st->len) {
+               st->len = st->pos;
+
+               if (st->len < st->size)
+                       st->string[st->len] = '\0';
+               else if (!st->update)
+                       st->string[st->size - 1] = '\0';
+       }
+
+       return (i);
+}
+
+static fpos_t
+fmemopen_seek(void *v, fpos_t off, int whence)
+{
+       struct state    *st = v;
+       ssize_t          base = 0;
+
+       switch (whence) {
+       case SEEK_SET:
+               break;
+       case SEEK_CUR:
+               base = st->pos;
+               break;
+       case SEEK_END:
+               base = st->len;
+               break;
+       }
+
+       if (off > st->size - base || off < -base) {
+               errno = EOVERFLOW;
+               return (-1);
+       }
+
+       st->pos = base + off;
+
+       return (st->pos);
+}
+
+static int
+fmemopen_close(void *v)
+{
+       free(v);
+
+       return (0);
+}
+
+static int
+fmemopen_close_free(void *v)
+{
+       struct state    *st = v;
+
+       free(st->string);
+       free(st);
+
+       return (0);
+}
+
+FILE *
+fmemopen(void *buf, size_t size, const char *mode)
+{
+       struct state    *st;
+       FILE            *fp;
+       int              flags, oflags;
+
+       if (size == 0) {
+               errno = EINVAL;
+               return (NULL);
+       }
+
+       if ((flags = __sflags(mode, &oflags)) == 0) {
+               errno = EINVAL;
+               return (NULL);
+       }
+
+       if (buf == NULL && ((oflags & O_RDWR) == 0)) {
+               errno = EINVAL;
+               return (NULL);
+       }
+
+       if ((st = malloc(sizeof(*st))) == NULL)
+               return (NULL);
+
+       if ((fp = __sfp()) == NULL) {
+               free(st);
+               return (NULL);
+       }
+
+       st->pos = 0;
+       st->len = (oflags & O_WRONLY) ? 0 : size;
+       st->size = size;
+       st->update = oflags & O_RDWR;
+
+       if (buf == NULL) {
+               if ((st->string = malloc(size)) == NULL) {
+                       free(st);
+                       fp->_flags = 0;
+                       return (NULL);
+               }
+               *st->string = '\0';
+       } else {
+               st->string = (char *)buf;
+
+               if (oflags & O_TRUNC)
+                       *st->string = '\0';
+
+               if (oflags & O_APPEND) {
+                       char    *p;
+
+                       if ((p = memchr(st->string, '\0', size)) != NULL)
+                               st->pos = st->len = (p - st->string);
+                       else
+                               st->pos = st->len = size;
+               }
+       }
+
+       fp->_flags = (short)flags;
+       fp->_file = -1;
+       fp->_cookie = (void *)st;
+       fp->_read = (flags & __SWR) ? NULL : fmemopen_read;
+       fp->_write = (flags & __SRD) ? NULL : fmemopen_write;
+       fp->_seek = fmemopen_seek;
+       fp->_close = (buf == NULL) ? fmemopen_close_free : fmemopen_close;
+
+       return (fp);
+}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/open_memstream.c b/libc/upstream-openbsd/lib/libc/stdio/open_memstream.c
new file mode 100644 (file)
index 0000000..4610535
--- /dev/null
@@ -0,0 +1,158 @@
+/*     $OpenBSD: open_memstream.c,v 1.3 2013/04/03 03:11:53 guenther Exp $     */
+
+/*
+ * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "local.h"
+
+struct state {
+       char             *string;       /* actual stream */
+       char            **pbuf;         /* point to the stream */
+       size_t           *psize;        /* point to min(pos, len) */
+       size_t            pos;          /* current position */
+       size_t            size;         /* number of allocated char */
+       size_t            len;          /* length of the data */
+};
+
+static int
+memstream_write(void *v, const char *b, int l)
+{
+       struct state    *st = v;
+       char            *p;
+       size_t           i, end;
+
+       end = (st->pos + l);
+
+       if (end >= st->size) {
+               /* 1.6 is (very) close to the golden ratio. */
+               size_t  sz = st->size * 8 / 5;
+
+               if (sz < end + 1)
+                       sz = end + 1;
+               p = realloc(st->string, sz);
+               if (!p)
+                       return (-1);
+               bzero(p + st->size, sz - st->size);
+               *st->pbuf = st->string = p;
+               st->size = sz;
+       }
+
+       for (i = 0; i < l; i++)
+               st->string[st->pos + i] = b[i];
+       st->pos += l;
+
+       if (st->pos > st->len) {
+               st->len = st->pos;
+               st->string[st->len] = '\0';
+       }
+
+       *st->psize = st->pos;
+
+       return (i);
+}
+
+static fpos_t
+memstream_seek(void *v, fpos_t off, int whence)
+{
+       struct state    *st = v;
+       ssize_t          base = 0;
+
+       switch (whence) {
+       case SEEK_SET:
+               break;
+       case SEEK_CUR:
+               base = st->pos;
+               break;
+       case SEEK_END:
+               base = st->len;
+               break;
+       }
+
+       if (off > SIZE_MAX - base || off < -base) {
+               errno = EOVERFLOW;
+               return (-1);
+       }
+
+       st->pos = base + off;
+       *st->psize = MIN(st->pos, st->len);
+
+       return (st->pos);
+}
+
+static int
+memstream_close(void *v)
+{
+       struct state    *st = v;
+
+       free(st);
+
+       return (0);
+}
+
+FILE *
+open_memstream(char **pbuf, size_t *psize)
+{
+       struct state    *st;
+       FILE            *fp;
+
+       if (pbuf == NULL || psize == NULL) {
+               errno = EINVAL;
+               return (NULL);
+       }
+
+       if ((st = malloc(sizeof(*st))) == NULL)
+               return (NULL);
+
+       if ((fp = __sfp()) == NULL) {
+               free(st);
+               return (NULL);
+       }
+
+       st->size = BUFSIZ;
+       if ((st->string = calloc(1, st->size)) == NULL) {
+               free(st);
+               fp->_flags = 0;
+               return (NULL);
+       }
+
+       *st->string = '\0';
+       st->pos = 0;
+       st->len = 0;
+       st->pbuf = pbuf;
+       st->psize = psize;
+
+       *pbuf = st->string;
+       *psize = st->len;
+
+       fp->_flags = __SWR;
+       fp->_file = -1;
+       fp->_cookie = st;
+       fp->_read = NULL;
+       fp->_write = memstream_write;
+       fp->_seek = memstream_seek;
+       fp->_close = memstream_close;
+       _SET_ORIENTATION(fp, -1);
+
+       return (fp);
+}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/open_wmemstream.c b/libc/upstream-openbsd/lib/libc/stdio/open_wmemstream.c
new file mode 100644 (file)
index 0000000..9414187
--- /dev/null
@@ -0,0 +1,169 @@
+/*     $OpenBSD: open_wmemstream.c,v 1.3 2014/03/06 07:28:21 gerhard Exp $     */
+
+/*
+ * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include "local.h"
+
+struct state {
+       wchar_t          *string;       /* actual stream */
+       wchar_t         **pbuf;         /* point to the stream */
+       size_t           *psize;        /* point to min(pos, len) */
+       size_t            pos;          /* current position */
+       size_t            size;         /* number of allocated wchar_t */
+       size_t            len;          /* length of the data */
+       mbstate_t         mbs;          /* conversion state of the stream */
+};
+
+static int
+wmemstream_write(void *v, const char *b, int l)
+{
+       struct state    *st = v;
+       wchar_t         *p;
+       size_t           nmc, len, end;
+
+       end = (st->pos + l);
+
+       if (end >= st->size) {
+               /* 1.6 is (very) close to the golden ratio. */
+               size_t  sz = st->size * 8 / 5;
+
+               if (sz < end + 1)
+                       sz = end + 1;
+               p = realloc(st->string, sz * sizeof(wchar_t));
+               if (!p)
+                       return (-1);
+               bzero(p + st->size, (sz - st->size) * sizeof(wchar_t));
+               *st->pbuf = st->string = p;
+               st->size = sz;
+       }
+
+       nmc = (st->size - st->pos) * sizeof(wchar_t);
+       len = mbsnrtowcs(st->string + st->pos, &b, nmc, l, &st->mbs);
+       if (len == (size_t)-1)
+               return (-1);
+       st->pos += len;
+
+       if (st->pos > st->len) {
+               st->len = st->pos;
+               st->string[st->len] = L'\0';
+       }
+
+       *st->psize = st->pos;
+
+       return (len);
+}
+
+static fpos_t
+wmemstream_seek(void *v, fpos_t off, int whence)
+{
+       struct state    *st = v;
+       ssize_t          base = 0;
+
+       switch (whence) {
+       case SEEK_SET:
+               break;
+       case SEEK_CUR:
+               base = st->pos;
+               break;
+       case SEEK_END:
+               base = st->len;
+               break;
+       }
+
+       if (off > (SIZE_MAX / sizeof(wchar_t)) - base || off < -base) {
+               errno = EOVERFLOW;
+               return (-1);
+       }
+
+       /*
+        * XXX Clearing mbs here invalidates shift state for state-
+        * dependent encodings, but they are not (yet) supported.
+        */
+       bzero(&st->mbs, sizeof(st->mbs));
+
+       st->pos = base + off;
+       *st->psize = MIN(st->pos, st->len);
+
+       return (st->pos);
+}
+
+static int
+wmemstream_close(void *v)
+{
+       struct state    *st = v;
+
+       free(st);
+
+       return (0);
+}
+
+FILE *
+open_wmemstream(wchar_t **pbuf, size_t *psize)
+{
+       struct state    *st;
+       FILE            *fp;
+
+       if (pbuf == NULL || psize == NULL) {
+               errno = EINVAL;
+               return (NULL);
+       }
+
+       if ((st = malloc(sizeof(*st))) == NULL)
+               return (NULL);
+
+       if ((fp = __sfp()) == NULL) {
+               free(st);
+               return (NULL);
+       }
+
+       st->size = BUFSIZ * sizeof(wchar_t);
+       if ((st->string = calloc(1, st->size)) == NULL) {
+               free(st);
+               fp->_flags = 0;
+               return (NULL);
+       }
+
+       *st->string = L'\0';
+       st->pos = 0;
+       st->len = 0;
+       st->pbuf = pbuf;
+       st->psize = psize;
+       bzero(&st->mbs, sizeof(st->mbs));
+
+       *pbuf = st->string;
+       *psize = st->len;
+
+       fp->_flags = __SWR;
+       fp->_file = -1;
+       fp->_cookie = st;
+       fp->_read = NULL;
+       fp->_write = wmemstream_write;
+       fp->_seek = wmemstream_seek;
+       fp->_close = wmemstream_close;
+       _SET_ORIENTATION(fp, 1);
+
+       return (fp);
+}
index bb86509..2cd0df2 100644 (file)
@@ -676,3 +676,82 @@ TEST(stdio, fpos_t_and_seek) {
 
   fclose(fp);
 }
+
+TEST(stdio, fmemopen) {
+  char buf[16];
+  memset(buf, 0, sizeof(buf));
+  FILE* fp = fmemopen(buf, sizeof(buf), "r+");
+  ASSERT_EQ('<', fputc('<', fp));
+  ASSERT_NE(EOF, fputs("abc>\n", fp));
+  fflush(fp);
+
+  ASSERT_STREQ("<abc>\n", buf);
+
+  rewind(fp);
+
+  char line[16];
+  char* s = fgets(line, sizeof(line), fp);
+  ASSERT_TRUE(s != NULL);
+  ASSERT_STREQ("<abc>\n", s);
+
+  fclose(fp);
+}
+
+TEST(stdio, fmemopen_NULL) {
+  FILE* fp = fmemopen(nullptr, 128, "r+");
+  ASSERT_NE(EOF, fputs("xyz\n", fp));
+
+  rewind(fp);
+
+  char line[16];
+  char* s = fgets(line, sizeof(line), fp);
+  ASSERT_TRUE(s != NULL);
+  ASSERT_STREQ("xyz\n", s);
+
+  fclose(fp);
+}
+
+TEST(stdio, fmemopen_EINVAL) {
+  char buf[16];
+
+  // Invalid size.
+  errno = 0;
+  ASSERT_EQ(nullptr, fmemopen(buf, 0, "r+"));
+  ASSERT_EQ(EINVAL, errno);
+
+  // No '+' with NULL buffer.
+  errno = 0;
+  ASSERT_EQ(nullptr, fmemopen(nullptr, 0, "r"));
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(stdio, open_memstream) {
+  char* p = nullptr;
+  size_t size = 0;
+  FILE* fp = open_memstream(&p, &size);
+  ASSERT_NE(EOF, fputs("hello, world!", fp));
+  fclose(fp);
+
+  ASSERT_STREQ("hello, world!", p);
+  ASSERT_EQ(strlen("hello, world!"), size);
+  free(p);
+}
+
+TEST(stdio, open_memstream_EINVAL) {
+#if defined(__BIONIC__)
+  char* p;
+  size_t size;
+
+  // Invalid buffer.
+  errno = 0;
+  ASSERT_EQ(nullptr, open_memstream(nullptr, &size));
+  ASSERT_EQ(EINVAL, errno);
+
+  // Invalid size.
+  errno = 0;
+  ASSERT_EQ(nullptr, open_memstream(&p, nullptr));
+  ASSERT_EQ(EINVAL, errno);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
index d02c4bf..760475f 100644 (file)
@@ -489,3 +489,34 @@ TEST(wchar, mbrtowc_15439554) {
   EXPECT_EQ(4U, n);
   EXPECT_EQ(L'𤭢', wc);
 }
+
+TEST(wchar, open_wmemstream) {
+  wchar_t* p = nullptr;
+  size_t size = 0;
+  FILE* fp = open_wmemstream(&p, &size);
+  ASSERT_NE(EOF, fputws(L"hello, world!", fp));
+  fclose(fp);
+
+  ASSERT_STREQ(L"hello, world!", p);
+  ASSERT_EQ(wcslen(L"hello, world!"), size);
+  free(p);
+}
+
+TEST(stdio, open_wmemstream_EINVAL) {
+#if defined(__BIONIC__)
+  wchar_t* p;
+  size_t size;
+
+  // Invalid buffer.
+  errno = 0;
+  ASSERT_EQ(nullptr, open_wmemstream(nullptr, &size));
+  ASSERT_EQ(EINVAL, errno);
+
+  // Invalid size.
+  errno = 0;
+  ASSERT_EQ(nullptr, open_wmemstream(&p, nullptr));
+  ASSERT_EQ(EINVAL, errno);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}