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