1 /*	$OpenBSD: open_wmemstream.c,v 1.8 2015/09/12 16:23:14 guenther Exp $	*/
2 
3 /*
4  * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <wchar.h>
26 #include "local.h"
27 
28 #define	MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
29 
30 struct state {
31 	wchar_t		 *string;	/* actual stream */
32 	wchar_t		**pbuf;		/* point to the stream */
33 	size_t		 *psize;	/* point to min(pos, len) */
34 	size_t		  pos;		/* current position */
35 	size_t		  size;		/* number of allocated wchar_t */
36 	size_t		  len;		/* length of the data */
37 	mbstate_t	  mbs;		/* conversion state of the stream */
38 };
39 
40 static int
wmemstream_write(void * v,const char * b,int l)41 wmemstream_write(void *v, const char *b, int l)
42 {
43 	struct state	*st = v;
44 	wchar_t		*p;
45 	size_t		 nmc, len, end;
46 
47 	end = (st->pos + l);
48 
49 	if (end >= st->size) {
50 		/* 1.6 is (very) close to the golden ratio. */
51 		size_t	sz = st->size * 8 / 5;
52 
53 		if (sz < end + 1)
54 			sz = end + 1;
55 		p = reallocarray(st->string, sz, sizeof(wchar_t));
56 		if (!p)
57 			return (-1);
58 		bzero(p + st->size, (sz - st->size) * sizeof(wchar_t));
59 		*st->pbuf = st->string = p;
60 		st->size = sz;
61 	}
62 
63 	nmc = (st->size - st->pos) * sizeof(wchar_t);
64 	len = mbsnrtowcs(st->string + st->pos, &b, nmc, l, &st->mbs);
65 	if (len == (size_t)-1)
66 		return (-1);
67 	st->pos += len;
68 
69 	if (st->pos > st->len) {
70 		st->len = st->pos;
71 		st->string[st->len] = L'\0';
72 	}
73 
74 	*st->psize = st->pos;
75 
76 	return (len);
77 }
78 
79 static fpos_t
wmemstream_seek(void * v,fpos_t off,int whence)80 wmemstream_seek(void *v, fpos_t off, int whence)
81 {
82 	struct state	*st = v;
83 	ssize_t		 base = 0;
84 
85 	switch (whence) {
86 	case SEEK_SET:
87 		break;
88 	case SEEK_CUR:
89 		base = st->pos;
90 		break;
91 	case SEEK_END:
92 		base = st->len;
93 		break;
94 	}
95 
96 	if (off > (SIZE_MAX / sizeof(wchar_t)) - base || off < -base) {
97 		errno = EOVERFLOW;
98 		return (-1);
99 	}
100 
101 	/*
102 	 * XXX Clearing mbs here invalidates shift state for state-
103 	 * dependent encodings, but they are not (yet) supported.
104 	 */
105 	bzero(&st->mbs, sizeof(st->mbs));
106 
107 	st->pos = base + off;
108 	*st->psize = MINIMUM(st->pos, st->len);
109 
110 	return (st->pos);
111 }
112 
113 static int
wmemstream_close(void * v)114 wmemstream_close(void *v)
115 {
116 	struct state	*st = v;
117 
118 	free(st);
119 
120 	return (0);
121 }
122 
123 FILE *
open_wmemstream(wchar_t ** pbuf,size_t * psize)124 open_wmemstream(wchar_t **pbuf, size_t *psize)
125 {
126 	struct state	*st;
127 	FILE		*fp;
128 
129 	if (pbuf == NULL || psize == NULL) {
130 		errno = EINVAL;
131 		return (NULL);
132 	}
133 
134 	if ((st = malloc(sizeof(*st))) == NULL)
135 		return (NULL);
136 
137 	if ((fp = __sfp()) == NULL) {
138 		free(st);
139 		return (NULL);
140 	}
141 
142 	st->size = BUFSIZ * sizeof(wchar_t);
143 	if ((st->string = calloc(1, st->size)) == NULL) {
144 		free(st);
145 		fp->_flags = 0;
146 		return (NULL);
147 	}
148 
149 	*st->string = L'\0';
150 	st->pos = 0;
151 	st->len = 0;
152 	st->pbuf = pbuf;
153 	st->psize = psize;
154 	bzero(&st->mbs, sizeof(st->mbs));
155 
156 	*pbuf = st->string;
157 	*psize = st->len;
158 
159 	fp->_flags = __SWR;
160 	fp->_file = -1;
161 	fp->_cookie = st;
162 	fp->_read = NULL;
163 	fp->_write = wmemstream_write;
164 	fp->_seek = wmemstream_seek;
165 	fp->_close = wmemstream_close;
166 	_SET_ORIENTATION(fp, 1);
167 
168 	return (fp);
169 }
170