xref: /dragonfly/lib/libc/stdio/open_wmemstream.c (revision 2c8c2922)
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