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
wmemstream_write(void * v,const char * b,int l)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
wmemstream_seek(void * v,fpos_t off,int whence)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
wmemstream_close(void * v)113 wmemstream_close(void *v)
114 {
115 struct state *st = v;
116
117 free(st);
118
119 return (0);
120 }
121
122 FILE *
open_wmemstream(wchar_t ** pbuf,size_t * psize)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