diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2013-03-27 15:06:26 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2013-03-27 15:06:26 +0000 |
commit | 9ff3ab3504dfe7e7d285e3e0766479704249542f (patch) | |
tree | 2c39532fcad910480e3fe4187b2a9cfba1071398 | |
parent | 433ae5c328ded540a4b83c38155ba58f59f60acd (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.c | 39 | ||||
-rw-r--r-- | lib/libc/stdio/open_memstream.c | 59 | ||||
-rw-r--r-- | lib/libc/stdio/open_wmemstream.c | 168 |
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); +} |