summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-09-03 19:49:46 -0400
committerRich Felker <dalias@aerifal.cx>2011-09-03 19:49:46 -0400
commit1461e027579c16a6bd95a264d9a1db75a4c3fa74 (patch)
tree452bbfbacd707fcfd6f39e354a34824f4c16230e /src
parentad70ba774fe4fda8379b59b2b353cbd451d693cc (diff)
downloadmusl-1461e027579c16a6bd95a264d9a1db75a4c3fa74.tar.gz
implement open_wmemstream
not heavily tested, but it seems to be correct, including the odd behavior that seeking is in terms of wide character count. this precludes any simple buffering, so we just make the stream unbuffered.
Diffstat (limited to 'src')
-rw-r--r--src/stdio/open_wmemstream.c95
1 files changed, 95 insertions, 0 deletions
diff --git a/src/stdio/open_wmemstream.c b/src/stdio/open_wmemstream.c
new file mode 100644
index 00000000..5402ca1a
--- /dev/null
+++ b/src/stdio/open_wmemstream.c
@@ -0,0 +1,95 @@
+#include "stdio_impl.h"
+
+struct cookie {
+ wchar_t **bufp;
+ size_t *sizep;
+ size_t pos;
+ wchar_t *buf;
+ size_t len;
+ size_t space;
+ mbstate_t mbs;
+};
+
+static off_t wms_seek(FILE *f, off_t off, int whence)
+{
+ ssize_t base;
+ struct cookie *c = f->cookie;
+ switch (whence) {
+ case SEEK_SET:
+ base = 0;
+ break;
+ case SEEK_CUR:
+ base = c->pos;
+ break;
+ case SEEK_END:
+ base = c->len;
+ break;
+ default:
+ fail:
+ errno = EINVAL;
+ return -1;
+ }
+ if (-off > base || off > SSIZE_MAX/4-base) goto fail;
+ memset(&c->mbs, 0, sizeof c->mbs);
+ return c->pos = base+off;
+}
+
+static size_t wms_write(FILE *f, const unsigned char *buf, size_t len)
+{
+ struct cookie *c = f->cookie;
+ size_t len2;
+ wchar_t *newbuf;
+ if (len > c->space - c->pos) {
+ len2 = 2*c->space+1 | c->space+len+1;
+ if (len2 > SSIZE_MAX/4) return 0;
+ newbuf = realloc(c->buf, len2*4);
+ if (!newbuf) return 0;
+ *c->bufp = c->buf = newbuf;
+ memset(c->buf + c->space, 0, len2 - c->space);
+ c->space = len2;
+ }
+
+ len2 = mbsnrtowcs(c->buf+c->pos, (void *)&buf, len, c->space-c->pos, &c->mbs);
+ if (len2 == -1) return 0;
+ c->pos += len2;
+ if (c->pos >= c->len) c->len = c->pos;
+ *c->sizep = c->pos;
+ return len;
+}
+
+static int wms_close(FILE *f)
+{
+ return 0;
+}
+
+FILE *open_wmemstream(wchar_t **bufp, size_t *sizep)
+{
+ FILE *f;
+ struct cookie *c;
+ if (!(f=malloc(sizeof *f + sizeof *c))) return 0;
+ memset(f, 0, sizeof *f + sizeof *c);
+ f->cookie = c = (void *)(f+1);
+
+ c->bufp = bufp;
+ c->sizep = sizep;
+ c->pos = c->len = c->space = 0;
+ c->buf = 0;
+
+ f->flags = F_NORD;
+ f->fd = -1;
+ f->buf = (void *)(c+1);
+ f->buf_size = 0;
+ f->lbf = EOF;
+ f->write = wms_write;
+ f->seek = wms_seek;
+ f->close = wms_close;
+
+ if (!libc.threaded) {
+ f->lock = -1;
+ f->next = libc.ofl_head;
+ if (libc.ofl_head) libc.ofl_head->prev = f;
+ libc.ofl_head = f;
+ }
+
+ return f;
+}