xref: /illumos-gate/usr/src/lib/libc/port/stdio/open_wmemstream.c (revision 4a38094c1dd4980a3761849bfbdf92ae62c568f5)
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