1 /* $OpenBSD: open_wmemstream.c,v 1.8 2015/09/12 16:23:14 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 2011 Martin Pieuchot <mpi (at) openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <stdio.h> 22 #include <stdint.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <wchar.h> 26 #include "local.h" 27 28 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 29 30 struct state { 31 wchar_t *string; /* actual stream */ 32 wchar_t **pbuf; /* point to the stream */ 33 size_t *psize; /* point to min(pos, len) */ 34 size_t pos; /* current position */ 35 size_t size; /* number of allocated wchar_t */ 36 size_t len; /* length of the data */ 37 mbstate_t mbs; /* conversion state of the stream */ 38 }; 39 40 static int 41 wmemstream_write(void *v, const char *b, int l) 42 { 43 struct state *st = v; 44 wchar_t *p; 45 size_t nmc, len, end; 46 47 end = (st->pos + l); 48 49 if (end >= st->size) { 50 /* 1.6 is (very) close to the golden ratio. */ 51 size_t sz = st->size * 8 / 5; 52 53 if (sz < end + 1) 54 sz = end + 1; 55 p = reallocarray(st->string, sz, sizeof(wchar_t)); 56 if (!p) 57 return (-1); 58 bzero(p + st->size, (sz - st->size) * sizeof(wchar_t)); 59 *st->pbuf = st->string = p; 60 st->size = sz; 61 } 62 63 nmc = (st->size - st->pos) * sizeof(wchar_t); 64 len = mbsnrtowcs(st->string + st->pos, &b, nmc, l, &st->mbs); 65 if (len == (size_t)-1) 66 return (-1); 67 st->pos += len; 68 69 if (st->pos > st->len) { 70 st->len = st->pos; 71 st->string[st->len] = L'\0'; 72 } 73 74 *st->psize = st->pos; 75 76 return (len); 77 } 78 79 static fpos_t 80 wmemstream_seek(void *v, fpos_t off, int whence) 81 { 82 struct state *st = v; 83 ssize_t base = 0; 84 85 switch (whence) { 86 case SEEK_SET: 87 break; 88 case SEEK_CUR: 89 base = st->pos; 90 break; 91 case SEEK_END: 92 base = st->len; 93 break; 94 } 95 96 if (off > (SIZE_MAX / sizeof(wchar_t)) - base || off < -base) { 97 errno = EOVERFLOW; 98 return (-1); 99 } 100 101 /* 102 * XXX Clearing mbs here invalidates shift state for state- 103 * dependent encodings, but they are not (yet) supported. 104 */ 105 bzero(&st->mbs, sizeof(st->mbs)); 106 107 st->pos = base + off; 108 *st->psize = MINIMUM(st->pos, st->len); 109 110 return (st->pos); 111 } 112 113 static int 114 wmemstream_close(void *v) 115 { 116 struct state *st = v; 117 118 free(st); 119 120 return (0); 121 } 122 123 FILE * 124 open_wmemstream(wchar_t **pbuf, size_t *psize) 125 { 126 struct state *st; 127 FILE *fp; 128 129 if (pbuf == NULL || psize == NULL) { 130 errno = EINVAL; 131 return (NULL); 132 } 133 134 if ((st = malloc(sizeof(*st))) == NULL) 135 return (NULL); 136 137 if ((fp = __sfp()) == NULL) { 138 free(st); 139 return (NULL); 140 } 141 142 st->size = BUFSIZ * sizeof(wchar_t); 143 if ((st->string = calloc(1, st->size)) == NULL) { 144 free(st); 145 fp->_flags = 0; 146 return (NULL); 147 } 148 149 *st->string = L'\0'; 150 st->pos = 0; 151 st->len = 0; 152 st->pbuf = pbuf; 153 st->psize = psize; 154 bzero(&st->mbs, sizeof(st->mbs)); 155 156 *pbuf = st->string; 157 *psize = st->len; 158 159 fp->_flags = __SWR; 160 fp->_file = -1; 161 fp->_cookie = st; 162 fp->_read = NULL; 163 fp->_write = wmemstream_write; 164 fp->_seek = wmemstream_seek; 165 fp->_close = wmemstream_close; 166 _SET_ORIENTATION(fp, 1); 167 168 return (fp); 169 } 170