1 /* $OpenBSD: open_wmemstream.c,v 1.10 2023/07/11 12:14:16 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2011 Martin Pieuchot <mpi@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 = recallocarray(st->string, st->size, sz, sizeof(wchar_t)); 56 if (!p) 57 return (-1); 58 *st->pbuf = st->string = p; 59 st->size = sz; 60 } 61 62 nmc = (st->size - st->pos) * sizeof(wchar_t); 63 len = mbsnrtowcs(st->string + st->pos, &b, nmc, l, &st->mbs); 64 if (len == (size_t)-1) 65 return (-1); 66 st->pos += len; 67 68 if (st->pos > st->len) { 69 st->len = st->pos; 70 st->string[st->len] = L'\0'; 71 } 72 73 *st->psize = st->pos; 74 75 return (len); 76 } 77 78 static fpos_t 79 wmemstream_seek(void *v, fpos_t off, int whence) 80 { 81 struct state *st = v; 82 ssize_t base = 0; 83 84 switch (whence) { 85 case SEEK_SET: 86 break; 87 case SEEK_CUR: 88 base = st->pos; 89 break; 90 case SEEK_END: 91 base = st->len; 92 break; 93 } 94 95 if (off > (SIZE_MAX / sizeof(wchar_t)) - base || off < -base) { 96 errno = EOVERFLOW; 97 return (-1); 98 } 99 100 /* 101 * XXX Clearing mbs here invalidates shift state for state- 102 * dependent encodings, but they are not (yet) supported. 103 */ 104 bzero(&st->mbs, sizeof(st->mbs)); 105 106 st->pos = base + off; 107 *st->psize = MINIMUM(st->pos, st->len); 108 109 return (st->pos); 110 } 111 112 static int 113 wmemstream_close(void *v) 114 { 115 struct state *st = v; 116 117 free(st); 118 119 return (0); 120 } 121 122 FILE * 123 open_wmemstream(wchar_t **pbuf, size_t *psize) 124 { 125 struct state *st; 126 FILE *fp; 127 128 if (pbuf == NULL || psize == NULL) { 129 errno = EINVAL; 130 return (NULL); 131 } 132 133 if ((st = malloc(sizeof(*st))) == NULL) 134 return (NULL); 135 136 if ((fp = __sfp()) == NULL) { 137 free(st); 138 return (NULL); 139 } 140 141 st->size = BUFSIZ * sizeof(wchar_t); 142 if ((st->string = calloc(1, st->size)) == NULL) { 143 free(st); 144 fp->_flags = 0; 145 return (NULL); 146 } 147 148 st->pos = 0; 149 st->len = 0; 150 st->pbuf = pbuf; 151 st->psize = psize; 152 bzero(&st->mbs, sizeof(st->mbs)); 153 154 *pbuf = st->string; 155 *psize = st->len; 156 157 fp->_flags = __SWR; 158 fp->_file = -1; 159 fp->_cookie = st; 160 fp->_read = NULL; 161 fp->_write = wmemstream_write; 162 fp->_seek = wmemstream_seek; 163 fp->_close = wmemstream_close; 164 _SET_ORIENTATION(fp, 1); 165 166 return (fp); 167 } 168