implement open_wmemstream
authorRich Felker <dalias@aerifal.cx>
Sat, 3 Sep 2011 23:49:46 +0000 (19:49 -0400)
committerRich Felker <dalias@aerifal.cx>
Sat, 3 Sep 2011 23:49:46 +0000 (19:49 -0400)
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.

include/wchar.h
src/stdio/open_wmemstream.c [new file with mode: 0644]

index 27f0016..c3d8b04 100644 (file)
@@ -133,6 +133,7 @@ size_t wcsftime (wchar_t *, size_t, const wchar_t *, const struct tm *);
 
 #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
  || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE)
 
 #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
  || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE)
+FILE *open_wmemstream(wchar_t **, size_t *);
 size_t mbsnrtowcs(wchar_t *, const char **, size_t, size_t, mbstate_t *);
 size_t wcsnrtombs(char *, const wchar_t **, size_t, size_t, mbstate_t *);
 #endif
 size_t mbsnrtowcs(wchar_t *, const char **, size_t, size_t, mbstate_t *);
 size_t wcsnrtombs(char *, const wchar_t **, size_t, size_t, mbstate_t *);
 #endif
diff --git a/src/stdio/open_wmemstream.c b/src/stdio/open_wmemstream.c
new file mode 100644 (file)
index 0000000..5402ca1
--- /dev/null
@@ -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;
+}