1*2c8c2922SSascha Wildner /*-
2*2c8c2922SSascha Wildner * Copyright (c) 2013 Hudson River Trading LLC
3*2c8c2922SSascha Wildner * Written by: John H. Baldwin <jhb@FreeBSD.org>
4*2c8c2922SSascha Wildner * All rights reserved.
5*2c8c2922SSascha Wildner *
6*2c8c2922SSascha Wildner * Redistribution and use in source and binary forms, with or without
7*2c8c2922SSascha Wildner * modification, are permitted provided that the following conditions
8*2c8c2922SSascha Wildner * are met:
9*2c8c2922SSascha Wildner * 1. Redistributions of source code must retain the above copyright
10*2c8c2922SSascha Wildner * notice, this list of conditions and the following disclaimer.
11*2c8c2922SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
12*2c8c2922SSascha Wildner * notice, this list of conditions and the following disclaimer in the
13*2c8c2922SSascha Wildner * documentation and/or other materials provided with the distribution.
14*2c8c2922SSascha Wildner *
15*2c8c2922SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*2c8c2922SSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*2c8c2922SSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*2c8c2922SSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*2c8c2922SSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*2c8c2922SSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*2c8c2922SSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*2c8c2922SSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*2c8c2922SSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*2c8c2922SSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*2c8c2922SSascha Wildner * SUCH DAMAGE.
26*2c8c2922SSascha Wildner *
27*2c8c2922SSascha Wildner * $FreeBSD: head/lib/libc/stdio/open_wmemstream.c 281887 2015-04-23 14:22:20Z jhb $
28*2c8c2922SSascha Wildner */
29*2c8c2922SSascha Wildner
30*2c8c2922SSascha Wildner #include "namespace.h"
31*2c8c2922SSascha Wildner #include <assert.h>
32*2c8c2922SSascha Wildner #include <errno.h>
33*2c8c2922SSascha Wildner #include <limits.h>
34*2c8c2922SSascha Wildner #include <stdio.h>
35*2c8c2922SSascha Wildner #include <stdlib.h>
36*2c8c2922SSascha Wildner #include <string.h>
37*2c8c2922SSascha Wildner #include <wchar.h>
38*2c8c2922SSascha Wildner #include "un-namespace.h"
39*2c8c2922SSascha Wildner
40*2c8c2922SSascha Wildner /* XXX: There is no FPOS_MAX. This assumes fpos_t is an off_t. */
41*2c8c2922SSascha Wildner #define FPOS_MAX OFF_MAX
42*2c8c2922SSascha Wildner
43*2c8c2922SSascha Wildner struct wmemstream {
44*2c8c2922SSascha Wildner wchar_t **bufp;
45*2c8c2922SSascha Wildner size_t *sizep;
46*2c8c2922SSascha Wildner ssize_t len;
47*2c8c2922SSascha Wildner fpos_t offset;
48*2c8c2922SSascha Wildner mbstate_t mbstate;
49*2c8c2922SSascha Wildner };
50*2c8c2922SSascha Wildner
51*2c8c2922SSascha Wildner static int
wmemstream_grow(struct wmemstream * ms,fpos_t newoff)52*2c8c2922SSascha Wildner wmemstream_grow(struct wmemstream *ms, fpos_t newoff)
53*2c8c2922SSascha Wildner {
54*2c8c2922SSascha Wildner wchar_t *buf;
55*2c8c2922SSascha Wildner ssize_t newsize;
56*2c8c2922SSascha Wildner
57*2c8c2922SSascha Wildner if (newoff < 0 || newoff >= SSIZE_MAX / sizeof(wchar_t))
58*2c8c2922SSascha Wildner newsize = SSIZE_MAX / sizeof(wchar_t) - 1;
59*2c8c2922SSascha Wildner else
60*2c8c2922SSascha Wildner newsize = newoff;
61*2c8c2922SSascha Wildner if (newsize > ms->len) {
62*2c8c2922SSascha Wildner buf = realloc(*ms->bufp, (newsize + 1) * sizeof(wchar_t));
63*2c8c2922SSascha Wildner if (buf != NULL) {
64*2c8c2922SSascha Wildner #ifdef DEBUG
65*2c8c2922SSascha Wildner fprintf(stderr, "WMS: %p growing from %zd to %zd\n",
66*2c8c2922SSascha Wildner ms, ms->len, newsize);
67*2c8c2922SSascha Wildner #endif
68*2c8c2922SSascha Wildner wmemset(buf + ms->len + 1, 0, newsize - ms->len);
69*2c8c2922SSascha Wildner *ms->bufp = buf;
70*2c8c2922SSascha Wildner ms->len = newsize;
71*2c8c2922SSascha Wildner return (1);
72*2c8c2922SSascha Wildner }
73*2c8c2922SSascha Wildner return (0);
74*2c8c2922SSascha Wildner }
75*2c8c2922SSascha Wildner return (1);
76*2c8c2922SSascha Wildner }
77*2c8c2922SSascha Wildner
78*2c8c2922SSascha Wildner static void
wmemstream_update(struct wmemstream * ms)79*2c8c2922SSascha Wildner wmemstream_update(struct wmemstream *ms)
80*2c8c2922SSascha Wildner {
81*2c8c2922SSascha Wildner
82*2c8c2922SSascha Wildner assert(ms->len >= 0 && ms->offset >= 0);
83*2c8c2922SSascha Wildner *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset;
84*2c8c2922SSascha Wildner }
85*2c8c2922SSascha Wildner
86*2c8c2922SSascha Wildner /*
87*2c8c2922SSascha Wildner * Based on a starting multibyte state and an input buffer, determine
88*2c8c2922SSascha Wildner * how many wchar_t's would be output. This doesn't use mbsnrtowcs()
89*2c8c2922SSascha Wildner * so that it can handle embedded null characters.
90*2c8c2922SSascha Wildner */
91*2c8c2922SSascha Wildner static size_t
wbuflen(const mbstate_t * state,const char * buf,int len)92*2c8c2922SSascha Wildner wbuflen(const mbstate_t *state, const char *buf, int len)
93*2c8c2922SSascha Wildner {
94*2c8c2922SSascha Wildner mbstate_t lenstate;
95*2c8c2922SSascha Wildner size_t charlen, count;
96*2c8c2922SSascha Wildner
97*2c8c2922SSascha Wildner count = 0;
98*2c8c2922SSascha Wildner lenstate = *state;
99*2c8c2922SSascha Wildner while (len > 0) {
100*2c8c2922SSascha Wildner charlen = mbrlen(buf, len, &lenstate);
101*2c8c2922SSascha Wildner if (charlen == (size_t)-1)
102*2c8c2922SSascha Wildner return (-1);
103*2c8c2922SSascha Wildner if (charlen == (size_t)-2)
104*2c8c2922SSascha Wildner break;
105*2c8c2922SSascha Wildner if (charlen == 0)
106*2c8c2922SSascha Wildner /* XXX: Not sure how else to handle this. */
107*2c8c2922SSascha Wildner charlen = 1;
108*2c8c2922SSascha Wildner len -= charlen;
109*2c8c2922SSascha Wildner buf += charlen;
110*2c8c2922SSascha Wildner count++;
111*2c8c2922SSascha Wildner }
112*2c8c2922SSascha Wildner return (count);
113*2c8c2922SSascha Wildner }
114*2c8c2922SSascha Wildner
115*2c8c2922SSascha Wildner static int
wmemstream_write(void * cookie,const char * buf,int len)116*2c8c2922SSascha Wildner wmemstream_write(void *cookie, const char *buf, int len)
117*2c8c2922SSascha Wildner {
118*2c8c2922SSascha Wildner struct wmemstream *ms;
119*2c8c2922SSascha Wildner ssize_t consumed, wlen;
120*2c8c2922SSascha Wildner size_t charlen;
121*2c8c2922SSascha Wildner
122*2c8c2922SSascha Wildner ms = cookie;
123*2c8c2922SSascha Wildner wlen = wbuflen(&ms->mbstate, buf, len);
124*2c8c2922SSascha Wildner if (wlen < 0) {
125*2c8c2922SSascha Wildner errno = EILSEQ;
126*2c8c2922SSascha Wildner return (-1);
127*2c8c2922SSascha Wildner }
128*2c8c2922SSascha Wildner if (!wmemstream_grow(ms, ms->offset + wlen))
129*2c8c2922SSascha Wildner return (-1);
130*2c8c2922SSascha Wildner
131*2c8c2922SSascha Wildner /*
132*2c8c2922SSascha Wildner * This copies characters one at a time rather than using
133*2c8c2922SSascha Wildner * mbsnrtowcs() so it can properly handle embedded null
134*2c8c2922SSascha Wildner * characters.
135*2c8c2922SSascha Wildner */
136*2c8c2922SSascha Wildner consumed = 0;
137*2c8c2922SSascha Wildner while (len > 0 && ms->offset < ms->len) {
138*2c8c2922SSascha Wildner charlen = mbrtowc(*ms->bufp + ms->offset, buf, len,
139*2c8c2922SSascha Wildner &ms->mbstate);
140*2c8c2922SSascha Wildner if (charlen == (size_t)-1) {
141*2c8c2922SSascha Wildner if (consumed == 0) {
142*2c8c2922SSascha Wildner errno = EILSEQ;
143*2c8c2922SSascha Wildner return (-1);
144*2c8c2922SSascha Wildner }
145*2c8c2922SSascha Wildner /* Treat it as a successful short write. */
146*2c8c2922SSascha Wildner break;
147*2c8c2922SSascha Wildner }
148*2c8c2922SSascha Wildner if (charlen == 0)
149*2c8c2922SSascha Wildner /* XXX: Not sure how else to handle this. */
150*2c8c2922SSascha Wildner charlen = 1;
151*2c8c2922SSascha Wildner if (charlen == (size_t)-2) {
152*2c8c2922SSascha Wildner consumed += len;
153*2c8c2922SSascha Wildner len = 0;
154*2c8c2922SSascha Wildner } else {
155*2c8c2922SSascha Wildner consumed += charlen;
156*2c8c2922SSascha Wildner buf += charlen;
157*2c8c2922SSascha Wildner len -= charlen;
158*2c8c2922SSascha Wildner ms->offset++;
159*2c8c2922SSascha Wildner }
160*2c8c2922SSascha Wildner }
161*2c8c2922SSascha Wildner wmemstream_update(ms);
162*2c8c2922SSascha Wildner #ifdef DEBUG
163*2c8c2922SSascha Wildner fprintf(stderr, "WMS: write(%p, %d) = %zd\n", ms, len, consumed);
164*2c8c2922SSascha Wildner #endif
165*2c8c2922SSascha Wildner return (consumed);
166*2c8c2922SSascha Wildner }
167*2c8c2922SSascha Wildner
168*2c8c2922SSascha Wildner static fpos_t
wmemstream_seek(void * cookie,fpos_t pos,int whence)169*2c8c2922SSascha Wildner wmemstream_seek(void *cookie, fpos_t pos, int whence)
170*2c8c2922SSascha Wildner {
171*2c8c2922SSascha Wildner struct wmemstream *ms;
172*2c8c2922SSascha Wildner fpos_t old;
173*2c8c2922SSascha Wildner
174*2c8c2922SSascha Wildner ms = cookie;
175*2c8c2922SSascha Wildner old = ms->offset;
176*2c8c2922SSascha Wildner switch (whence) {
177*2c8c2922SSascha Wildner case SEEK_SET:
178*2c8c2922SSascha Wildner /* _fseeko() checks for negative offsets. */
179*2c8c2922SSascha Wildner assert(pos >= 0);
180*2c8c2922SSascha Wildner ms->offset = pos;
181*2c8c2922SSascha Wildner break;
182*2c8c2922SSascha Wildner case SEEK_CUR:
183*2c8c2922SSascha Wildner /* This is only called by _ftello(). */
184*2c8c2922SSascha Wildner assert(pos == 0);
185*2c8c2922SSascha Wildner break;
186*2c8c2922SSascha Wildner case SEEK_END:
187*2c8c2922SSascha Wildner if (pos < 0) {
188*2c8c2922SSascha Wildner if (pos + ms->len < 0) {
189*2c8c2922SSascha Wildner #ifdef DEBUG
190*2c8c2922SSascha Wildner fprintf(stderr,
191*2c8c2922SSascha Wildner "WMS: bad SEEK_END: pos %jd, len %zd\n",
192*2c8c2922SSascha Wildner (intmax_t)pos, ms->len);
193*2c8c2922SSascha Wildner #endif
194*2c8c2922SSascha Wildner errno = EINVAL;
195*2c8c2922SSascha Wildner return (-1);
196*2c8c2922SSascha Wildner }
197*2c8c2922SSascha Wildner } else {
198*2c8c2922SSascha Wildner if (FPOS_MAX - ms->len < pos) {
199*2c8c2922SSascha Wildner #ifdef DEBUG
200*2c8c2922SSascha Wildner fprintf(stderr,
201*2c8c2922SSascha Wildner "WMS: bad SEEK_END: pos %jd, len %zd\n",
202*2c8c2922SSascha Wildner (intmax_t)pos, ms->len);
203*2c8c2922SSascha Wildner #endif
204*2c8c2922SSascha Wildner errno = EOVERFLOW;
205*2c8c2922SSascha Wildner return (-1);
206*2c8c2922SSascha Wildner }
207*2c8c2922SSascha Wildner }
208*2c8c2922SSascha Wildner ms->offset = ms->len + pos;
209*2c8c2922SSascha Wildner break;
210*2c8c2922SSascha Wildner }
211*2c8c2922SSascha Wildner /* Reset the multibyte state if a seek changes the position. */
212*2c8c2922SSascha Wildner if (ms->offset != old)
213*2c8c2922SSascha Wildner memset(&ms->mbstate, 0, sizeof(ms->mbstate));
214*2c8c2922SSascha Wildner wmemstream_update(ms);
215*2c8c2922SSascha Wildner #ifdef DEBUG
216*2c8c2922SSascha Wildner fprintf(stderr, "WMS: seek(%p, %jd, %d) %jd -> %jd\n", ms,
217*2c8c2922SSascha Wildner (intmax_t)pos, whence, (intmax_t)old, (intmax_t)ms->offset);
218*2c8c2922SSascha Wildner #endif
219*2c8c2922SSascha Wildner return (ms->offset);
220*2c8c2922SSascha Wildner }
221*2c8c2922SSascha Wildner
222*2c8c2922SSascha Wildner static int
wmemstream_close(void * cookie)223*2c8c2922SSascha Wildner wmemstream_close(void *cookie)
224*2c8c2922SSascha Wildner {
225*2c8c2922SSascha Wildner
226*2c8c2922SSascha Wildner free(cookie);
227*2c8c2922SSascha Wildner return (0);
228*2c8c2922SSascha Wildner }
229*2c8c2922SSascha Wildner
230*2c8c2922SSascha Wildner FILE *
open_wmemstream(wchar_t ** bufp,size_t * sizep)231*2c8c2922SSascha Wildner open_wmemstream(wchar_t **bufp, size_t *sizep)
232*2c8c2922SSascha Wildner {
233*2c8c2922SSascha Wildner struct wmemstream *ms;
234*2c8c2922SSascha Wildner int save_errno;
235*2c8c2922SSascha Wildner FILE *fp;
236*2c8c2922SSascha Wildner
237*2c8c2922SSascha Wildner if (bufp == NULL || sizep == NULL) {
238*2c8c2922SSascha Wildner errno = EINVAL;
239*2c8c2922SSascha Wildner return (NULL);
240*2c8c2922SSascha Wildner }
241*2c8c2922SSascha Wildner *bufp = calloc(1, sizeof(wchar_t));
242*2c8c2922SSascha Wildner if (*bufp == NULL)
243*2c8c2922SSascha Wildner return (NULL);
244*2c8c2922SSascha Wildner ms = malloc(sizeof(*ms));
245*2c8c2922SSascha Wildner if (ms == NULL) {
246*2c8c2922SSascha Wildner save_errno = errno;
247*2c8c2922SSascha Wildner free(*bufp);
248*2c8c2922SSascha Wildner *bufp = NULL;
249*2c8c2922SSascha Wildner errno = save_errno;
250*2c8c2922SSascha Wildner return (NULL);
251*2c8c2922SSascha Wildner }
252*2c8c2922SSascha Wildner ms->bufp = bufp;
253*2c8c2922SSascha Wildner ms->sizep = sizep;
254*2c8c2922SSascha Wildner ms->len = 0;
255*2c8c2922SSascha Wildner ms->offset = 0;
256*2c8c2922SSascha Wildner memset(&ms->mbstate, 0, sizeof(mbstate_t));
257*2c8c2922SSascha Wildner wmemstream_update(ms);
258*2c8c2922SSascha Wildner fp = funopen(ms, NULL, wmemstream_write, wmemstream_seek,
259*2c8c2922SSascha Wildner wmemstream_close);
260*2c8c2922SSascha Wildner if (fp == NULL) {
261*2c8c2922SSascha Wildner save_errno = errno;
262*2c8c2922SSascha Wildner free(ms);
263*2c8c2922SSascha Wildner free(*bufp);
264*2c8c2922SSascha Wildner *bufp = NULL;
265*2c8c2922SSascha Wildner errno = save_errno;
266*2c8c2922SSascha Wildner return (NULL);
267*2c8c2922SSascha Wildner }
268*2c8c2922SSascha Wildner fwide(fp, 1);
269*2c8c2922SSascha Wildner return (fp);
270*2c8c2922SSascha Wildner }
271