1cd62a92dSRobert Mustacchi /*
2cd62a92dSRobert Mustacchi * This file and its contents are supplied under the terms of the
3cd62a92dSRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4cd62a92dSRobert Mustacchi * You may only use this file in accordance with the terms of version
5cd62a92dSRobert Mustacchi * 1.0 of the CDDL.
6cd62a92dSRobert Mustacchi *
7cd62a92dSRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8cd62a92dSRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9cd62a92dSRobert Mustacchi * http://www.illumos.org/license/CDDL.
10cd62a92dSRobert Mustacchi */
11cd62a92dSRobert Mustacchi
12cd62a92dSRobert Mustacchi /*
13cd62a92dSRobert Mustacchi * Copyright 2020 Robert Mustacchi
14cd62a92dSRobert Mustacchi */
15cd62a92dSRobert Mustacchi
16cd62a92dSRobert Mustacchi /*
17cd62a92dSRobert Mustacchi * Implements open_wmemstream(3C).
18cd62a92dSRobert Mustacchi */
19cd62a92dSRobert Mustacchi
20cd62a92dSRobert Mustacchi #include "mtlib.h"
21cd62a92dSRobert Mustacchi #include "file64.h"
22cd62a92dSRobert Mustacchi #include <stdio.h>
23cd62a92dSRobert Mustacchi #include "stdiom.h"
24cd62a92dSRobert Mustacchi #include <errno.h>
25cd62a92dSRobert Mustacchi #include <stdlib.h>
26cd62a92dSRobert Mustacchi #include <fcntl.h>
27cd62a92dSRobert Mustacchi #include <sys/sysmacros.h>
28cd62a92dSRobert Mustacchi #include <limits.h>
29cd62a92dSRobert Mustacchi #include "libc.h"
30cd62a92dSRobert Mustacchi
31cd62a92dSRobert Mustacchi typedef struct wmemstream {
32cd62a92dSRobert Mustacchi wchar_t *wmstr_buf;
33cd62a92dSRobert Mustacchi size_t wmstr_alloc;
34cd62a92dSRobert Mustacchi size_t wmstr_pos;
35cd62a92dSRobert Mustacchi size_t wmstr_lsize;
36cd62a92dSRobert Mustacchi mbstate_t wmstr_mbs;
37cd62a92dSRobert Mustacchi wchar_t **wmstr_ubufp;
38cd62a92dSRobert Mustacchi size_t *wmstr_usizep;
39cd62a92dSRobert Mustacchi } wmemstream_t;
40cd62a92dSRobert Mustacchi
41cd62a92dSRobert Mustacchi #define WMEMSTREAM_MAX (SSIZE_MAX / sizeof (wchar_t))
42cd62a92dSRobert Mustacchi
43cd62a92dSRobert Mustacchi /*
44cd62a92dSRobert Mustacchi * The SUSv4 spec says that this should not support reads.
45cd62a92dSRobert Mustacchi */
46cd62a92dSRobert Mustacchi static ssize_t
open_wmemstream_read(FILE * iop __unused,char * buf __unused,size_t nbytes __unused)47*4a38094cSToomas Soome open_wmemstream_read(FILE *iop __unused, char *buf __unused,
48*4a38094cSToomas Soome size_t nbytes __unused)
49cd62a92dSRobert Mustacchi {
50cd62a92dSRobert Mustacchi errno = EBADF;
51cd62a92dSRobert Mustacchi return (-1);
52cd62a92dSRobert Mustacchi }
53cd62a92dSRobert Mustacchi
54cd62a92dSRobert Mustacchi static ssize_t
open_wmemstream_write(FILE * iop,const char * buf,size_t nbytes)55cd62a92dSRobert Mustacchi open_wmemstream_write(FILE *iop, const char *buf, size_t nbytes)
56cd62a92dSRobert Mustacchi {
57cd62a92dSRobert Mustacchi wmemstream_t *wmemp = _xdata(iop);
588a67df42SToomas Soome size_t newsize;
59cd62a92dSRobert Mustacchi ssize_t nwritten = 0;
60cd62a92dSRobert Mustacchi int ret;
61cd62a92dSRobert Mustacchi
62cd62a92dSRobert Mustacchi /*
63cd62a92dSRobert Mustacchi * nbytes is in bytes not wide characters. However, the most
64cd62a92dSRobert Mustacchi * pathological case from a writing perspective is using ASCII
65cd62a92dSRobert Mustacchi * characters. Thus if we size things assuming that nbytes will all
66cd62a92dSRobert Mustacchi * possibly be valid wchar_t values on their own, then we'll always have
67cd62a92dSRobert Mustacchi * enough buffer space.
68cd62a92dSRobert Mustacchi */
69cd62a92dSRobert Mustacchi nbytes = MIN(nbytes, WMEMSTREAM_MAX);
70cd62a92dSRobert Mustacchi ret = memstream_newsize(wmemp->wmstr_pos, wmemp->wmstr_alloc, nbytes,
71cd62a92dSRobert Mustacchi &newsize);
72cd62a92dSRobert Mustacchi if (ret < 0) {
73cd62a92dSRobert Mustacchi return (-1);
74cd62a92dSRobert Mustacchi } else if (ret > 0) {
75cd62a92dSRobert Mustacchi void *temp;
76cd62a92dSRobert Mustacchi temp = recallocarray(wmemp->wmstr_buf, wmemp->wmstr_alloc,
77cd62a92dSRobert Mustacchi newsize, sizeof (wchar_t));
78cd62a92dSRobert Mustacchi if (temp == NULL) {
79cd62a92dSRobert Mustacchi return (-1);
80cd62a92dSRobert Mustacchi }
81cd62a92dSRobert Mustacchi wmemp->wmstr_buf = temp;
82cd62a92dSRobert Mustacchi wmemp->wmstr_alloc = newsize;
83cd62a92dSRobert Mustacchi *wmemp->wmstr_ubufp = temp;
84cd62a92dSRobert Mustacchi
85cd62a92dSRobert Mustacchi }
86cd62a92dSRobert Mustacchi
87cd62a92dSRobert Mustacchi while (nbytes > 0) {
88cd62a92dSRobert Mustacchi size_t nchars;
89cd62a92dSRobert Mustacchi
90cd62a92dSRobert Mustacchi nchars = mbrtowc_nz(&wmemp->wmstr_buf[wmemp->wmstr_pos],
91cd62a92dSRobert Mustacchi &buf[nwritten], nbytes, &wmemp->wmstr_mbs);
92cd62a92dSRobert Mustacchi if (nchars == (size_t)-1) {
93cd62a92dSRobert Mustacchi if (nwritten > 0) {
94cd62a92dSRobert Mustacchi errno = 0;
95cd62a92dSRobert Mustacchi break;
96cd62a92dSRobert Mustacchi } else {
97cd62a92dSRobert Mustacchi /*
98cd62a92dSRobert Mustacchi * Overwrite errno in this case to be EIO. Most
99cd62a92dSRobert Mustacchi * callers of stdio routines don't expect
100cd62a92dSRobert Mustacchi * EILSEQ and it's not documented in POSIX, so
101cd62a92dSRobert Mustacchi * we use this instead.
102cd62a92dSRobert Mustacchi */
103cd62a92dSRobert Mustacchi errno = EIO;
104cd62a92dSRobert Mustacchi return (-1);
105cd62a92dSRobert Mustacchi }
106cd62a92dSRobert Mustacchi } else if (nchars == (size_t)-2) {
107cd62a92dSRobert Mustacchi nwritten += nbytes;
108cd62a92dSRobert Mustacchi nbytes = 0;
109cd62a92dSRobert Mustacchi } else {
110cd62a92dSRobert Mustacchi nwritten += nchars;
111cd62a92dSRobert Mustacchi nbytes -= nchars;
112cd62a92dSRobert Mustacchi wmemp->wmstr_pos++;
113cd62a92dSRobert Mustacchi }
114cd62a92dSRobert Mustacchi }
115cd62a92dSRobert Mustacchi
116cd62a92dSRobert Mustacchi if (wmemp->wmstr_pos > wmemp->wmstr_lsize) {
117cd62a92dSRobert Mustacchi wmemp->wmstr_lsize = wmemp->wmstr_pos;
118cd62a92dSRobert Mustacchi wmemp->wmstr_buf[wmemp->wmstr_pos] = L'\0';
119cd62a92dSRobert Mustacchi }
120cd62a92dSRobert Mustacchi *wmemp->wmstr_usizep = MIN(wmemp->wmstr_pos, wmemp->wmstr_lsize);
121cd62a92dSRobert Mustacchi return (nwritten);
122cd62a92dSRobert Mustacchi }
123cd62a92dSRobert Mustacchi
124cd62a92dSRobert Mustacchi static off_t
open_wmemstream_seek(FILE * iop,off_t off,int whence)125cd62a92dSRobert Mustacchi open_wmemstream_seek(FILE *iop, off_t off, int whence)
126cd62a92dSRobert Mustacchi {
127cd62a92dSRobert Mustacchi wmemstream_t *wmemp = _xdata(iop);
128cd62a92dSRobert Mustacchi size_t base, npos;
129cd62a92dSRobert Mustacchi
130cd62a92dSRobert Mustacchi switch (whence) {
131cd62a92dSRobert Mustacchi case SEEK_SET:
132cd62a92dSRobert Mustacchi base = 0;
133cd62a92dSRobert Mustacchi break;
134cd62a92dSRobert Mustacchi case SEEK_CUR:
135cd62a92dSRobert Mustacchi base = wmemp->wmstr_pos;
136cd62a92dSRobert Mustacchi break;
137cd62a92dSRobert Mustacchi case SEEK_END:
138cd62a92dSRobert Mustacchi base = wmemp->wmstr_lsize;
139cd62a92dSRobert Mustacchi break;
140cd62a92dSRobert Mustacchi default:
141cd62a92dSRobert Mustacchi errno = EINVAL;
142cd62a92dSRobert Mustacchi return (-1);
143cd62a92dSRobert Mustacchi }
144cd62a92dSRobert Mustacchi
145cd62a92dSRobert Mustacchi if (!memstream_seek(base, off, WMEMSTREAM_MAX, &npos)) {
146cd62a92dSRobert Mustacchi errno = EINVAL;
147cd62a92dSRobert Mustacchi return (-1);
148cd62a92dSRobert Mustacchi }
149cd62a92dSRobert Mustacchi
150cd62a92dSRobert Mustacchi wmemp->wmstr_pos = npos;
151cd62a92dSRobert Mustacchi *wmemp->wmstr_usizep = MIN(wmemp->wmstr_pos, wmemp->wmstr_lsize);
152cd62a92dSRobert Mustacchi
153cd62a92dSRobert Mustacchi return ((off_t)wmemp->wmstr_pos);
154cd62a92dSRobert Mustacchi }
155cd62a92dSRobert Mustacchi
156cd62a92dSRobert Mustacchi static int
open_wmemstream_close(FILE * iop)157cd62a92dSRobert Mustacchi open_wmemstream_close(FILE *iop)
158cd62a92dSRobert Mustacchi {
159cd62a92dSRobert Mustacchi wmemstream_t *wmemp = _xdata(iop);
160cd62a92dSRobert Mustacchi free(wmemp);
161cd62a92dSRobert Mustacchi _xunassoc(iop);
162cd62a92dSRobert Mustacchi return (0);
163cd62a92dSRobert Mustacchi }
164cd62a92dSRobert Mustacchi
165cd62a92dSRobert Mustacchi
166cd62a92dSRobert Mustacchi FILE *
open_wmemstream(wchar_t ** bufp,size_t * sizep)167cd62a92dSRobert Mustacchi open_wmemstream(wchar_t **bufp, size_t *sizep)
168cd62a92dSRobert Mustacchi {
169cd62a92dSRobert Mustacchi FILE *iop;
170cd62a92dSRobert Mustacchi wmemstream_t *wmemp;
171cd62a92dSRobert Mustacchi
172cd62a92dSRobert Mustacchi if (bufp == NULL || sizep == NULL) {
173cd62a92dSRobert Mustacchi errno = EINVAL;
174cd62a92dSRobert Mustacchi return (NULL);
175cd62a92dSRobert Mustacchi }
176cd62a92dSRobert Mustacchi
177cd62a92dSRobert Mustacchi wmemp = calloc(1, sizeof (wmemstream_t));
178cd62a92dSRobert Mustacchi if (wmemp == NULL) {
179cd62a92dSRobert Mustacchi return (NULL);
180cd62a92dSRobert Mustacchi }
181cd62a92dSRobert Mustacchi
182cd62a92dSRobert Mustacchi wmemp->wmstr_alloc = BUFSIZ;
183cd62a92dSRobert Mustacchi wmemp->wmstr_buf = calloc(wmemp->wmstr_alloc, sizeof (wchar_t));
184cd62a92dSRobert Mustacchi if (wmemp->wmstr_buf == NULL) {
185cd62a92dSRobert Mustacchi goto cleanup;
186cd62a92dSRobert Mustacchi }
187cd62a92dSRobert Mustacchi wmemp->wmstr_buf[0] = L'\0';
188cd62a92dSRobert Mustacchi wmemp->wmstr_pos = 0;
189cd62a92dSRobert Mustacchi wmemp->wmstr_lsize = 0;
190cd62a92dSRobert Mustacchi wmemp->wmstr_ubufp = bufp;
191cd62a92dSRobert Mustacchi wmemp->wmstr_usizep = sizep;
192cd62a92dSRobert Mustacchi
193cd62a92dSRobert Mustacchi iop = _findiop();
194cd62a92dSRobert Mustacchi if (iop == NULL) {
195cd62a92dSRobert Mustacchi goto cleanup;
196cd62a92dSRobert Mustacchi }
197cd62a92dSRobert Mustacchi
198cd62a92dSRobert Mustacchi #ifdef _LP64
199cd62a92dSRobert Mustacchi iop->_flag = (iop->_flag & ~_DEF_FLAG_MASK) | _IOWRT;
200cd62a92dSRobert Mustacchi #else
201cd62a92dSRobert Mustacchi iop->_flag = _IOWRT;
202cd62a92dSRobert Mustacchi #endif
203cd62a92dSRobert Mustacchi
204cd62a92dSRobert Mustacchi /*
205cd62a92dSRobert Mustacchi * Update the user pointers now, in case a call to fflush() happens
206cd62a92dSRobert Mustacchi * immediately.
207cd62a92dSRobert Mustacchi */
208cd62a92dSRobert Mustacchi
209cd62a92dSRobert Mustacchi if (_xassoc(iop, open_wmemstream_read, open_wmemstream_write,
210cd62a92dSRobert Mustacchi open_wmemstream_seek, open_wmemstream_close, wmemp) != 0) {
211cd62a92dSRobert Mustacchi goto cleanup;
212cd62a92dSRobert Mustacchi }
213cd62a92dSRobert Mustacchi _setorientation(iop, _WC_MODE);
214cd62a92dSRobert Mustacchi SET_SEEKABLE(iop);
215cd62a92dSRobert Mustacchi
216cd62a92dSRobert Mustacchi *wmemp->wmstr_ubufp = wmemp->wmstr_buf;
217cd62a92dSRobert Mustacchi *wmemp->wmstr_usizep = MIN(wmemp->wmstr_pos, wmemp->wmstr_lsize);
218cd62a92dSRobert Mustacchi
219cd62a92dSRobert Mustacchi return (iop);
220cd62a92dSRobert Mustacchi
221cd62a92dSRobert Mustacchi cleanup:
222cd62a92dSRobert Mustacchi free(wmemp->wmstr_buf);
223cd62a92dSRobert Mustacchi free(wmemp);
224cd62a92dSRobert Mustacchi return (NULL);
225cd62a92dSRobert Mustacchi }
226