summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Pieuchot <mpi@cvs.openbsd.org>2013-03-27 15:06:26 +0000
committerMartin Pieuchot <mpi@cvs.openbsd.org>2013-03-27 15:06:26 +0000
commit9ff3ab3504dfe7e7d285e3e0766479704249542f (patch)
tree2c39532fcad910480e3fe4187b2a9cfba1071398
parent433ae5c328ded540a4b83c38155ba58f59f60acd (diff)
Add an open_wmemstream(3) implementation and fix various issues for
fmemopen(3) and open_memstream(3). With inputs from millert@, stsp@, guenther@, tedu@ and matthew@
-rw-r--r--lib/libc/stdio/fmemopen.c39
-rw-r--r--lib/libc/stdio/open_memstream.c59
-rw-r--r--lib/libc/stdio/open_wmemstream.c168
3 files changed, 211 insertions, 55 deletions
diff --git a/lib/libc/stdio/fmemopen.c b/lib/libc/stdio/fmemopen.c
index fd76f625b23..8cda0476340 100644
--- a/lib/libc/stdio/fmemopen.c
+++ b/lib/libc/stdio/fmemopen.c
@@ -1,4 +1,5 @@
-/* $OpenBSD: fmemopen.c,v 1.1 2013/01/01 17:41:13 mpi Exp $ */
+/* $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
@@ -20,6 +21,7 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include "local.h"
struct state {
@@ -27,6 +29,7 @@ struct state {
size_t pos; /* current position */
size_t size; /* allocated size */
size_t len; /* length of the data */
+ int update; /* open for update */
};
static int
@@ -55,44 +58,40 @@ fmemopen_write(void *v, const char *b, int l)
if (st->pos >= st->len) {
st->len = st->pos;
- if (st->len == st->size)
- st->string[st->len - 1] = '\0';
- else
+ 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 pos, int whence)
+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:
- pos += st->pos;
+ base = st->pos;
break;
case SEEK_END:
- /*
- * XXX The standard is not clear about where to seek
- * from the end of the data or the end of the buffer.
- */
- pos += st->len;
+ base = st->len;
break;
- default:
- errno = EINVAL;
- return (-1);
}
- if (pos < 0 || pos > st->size)
+ if (off > st->size - base || off < -base) {
+ errno = EOVERFLOW;
return (-1);
+ }
- st->pos = pos;
+ st->pos = base + off;
- return (pos);
+ return (st->pos);
}
static int
@@ -145,8 +144,9 @@ fmemopen(void *buf, size_t size, const char *mode)
}
st->pos = 0;
- st->len = 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) {
@@ -158,9 +158,6 @@ fmemopen(void *buf, size_t size, const char *mode)
} else {
st->string = (char *)buf;
- if ((oflags & O_WRONLY) == 0)
- st->len = size;
-
if (oflags & O_TRUNC)
*st->string = '\0';
diff --git a/lib/libc/stdio/open_memstream.c b/lib/libc/stdio/open_memstream.c
index 48ae2900f6d..bd96874e959 100644
--- a/lib/libc/stdio/open_memstream.c
+++ b/lib/libc/stdio/open_memstream.c
@@ -1,4 +1,5 @@
-/* $OpenBSD: open_memstream.c,v 1.1 2013/01/01 17:41:13 mpi Exp $ */
+/* $OpenBSD: open_memstream.c,v 1.2 2013/03/27 15:06:25 mpi Exp $ */
+
/*
* Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
*
@@ -15,8 +16,11 @@
* 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>
@@ -27,7 +31,7 @@ struct state {
char **pbuf; /* point to the stream */
size_t *psize; /* point to min(pos, len) */
size_t pos; /* current position */
- size_t size; /* allocated size */
+ size_t size; /* number of allocated char */
size_t len; /* length of the data */
};
@@ -35,15 +39,17 @@ static int
memstream_write(void *v, const char *b, int l)
{
struct state *st = v;
- int i;
char *p;
+ size_t i, end;
- if (st->pos + l >= st->size) {
+ 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 < st->pos + l + 1)
- sz = st->pos + l + 1;
+ if (sz < end + 1)
+ sz = end + 1;
p = realloc(st->string, sz);
if (!p)
return (-1);
@@ -54,7 +60,7 @@ memstream_write(void *v, const char *b, int l)
for (i = 0; i < l; i++)
st->string[st->pos + i] = b[i];
- st->pos += i;
+ st->pos += l;
if (st->pos > st->len) {
st->len = st->pos;
@@ -67,52 +73,41 @@ memstream_write(void *v, const char *b, int l)
}
static fpos_t
-memstream_seek(void *v, fpos_t pos, int w)
+memstream_seek(void *v, fpos_t off, int whence)
{
struct state *st = v;
- char *p;
+ ssize_t base = 0;
- switch (w) {
+ switch (whence) {
case SEEK_SET:
break;
case SEEK_CUR:
- pos += st->pos;
+ base = st->pos;
break;
case SEEK_END:
- pos += st->len;
+ base = st->len;
break;
- default:
- errno = EINVAL;
- return (-1);
}
- if (pos < 0)
+ if (off > SIZE_MAX - base || off < -base) {
+ errno = EOVERFLOW;
return (-1);
+ }
- st->pos = pos;
-
- if (st->pos < st->len)
- *st->psize = st->pos;
- else
- *st->psize = st->len;
+ st->pos = base + off;
+ *st->psize = MIN(st->pos, st->len);
- return (pos);
+ return (st->pos);
}
static int
memstream_close(void *v)
{
struct state *st = v;
- char *p;
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-
- *st->psize = MIN(st->pos, st->len);
- *st->pbuf = st->string;
free(st);
return (0);
-#undef MIN
}
FILE *
@@ -134,11 +129,7 @@ open_memstream(char **pbuf, size_t *psize)
return (NULL);
}
- if (*psize < 128)
- st->size = 128;
- else
- st->size = *psize;
-
+ st->size = BUFSIZ;
if ((st->string = calloc(1, st->size)) == NULL) {
free(st);
fp->_flags = 0;
diff --git a/lib/libc/stdio/open_wmemstream.c b/lib/libc/stdio/open_wmemstream.c
new file mode 100644
index 00000000000..2b8677c3b6a
--- /dev/null
+++ b/lib/libc/stdio/open_wmemstream.c
@@ -0,0 +1,168 @@
+/* $OpenBSD: open_wmemstream.c,v 1.1 2013/03/27 15:06:25 mpi 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 < 0)
+ return (len);
+ 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;
+
+ return (fp);
+}