1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 #include "streams_wide.h"
29 #include "streams_common.h"
30
31 #define WIDE_VBUF_SIZE (64 * KILOBYTE)
32
33 #define SHELF_OCCUPIED 1
34 #define SHELF_VACANT 0
35 static int shelf = SHELF_VACANT;
36
37 /*
38 * Wide character streams implementation
39 *
40 * The wide character streams implementation is, for the most part, a
41 * reimplementation of the stdio streams implementation, using wide character
42 * string routines. However, fgetws(3C) retains the newline that fgets(3C)
43 * discards while reading a complete line. As a result, the wide character
44 * routines need to guard against coincidental exhaustion of the buffer, as
45 * well as overwriting the end-of-line character and correcting the
46 * l_data_length field.
47 */
48
49 static int
stream_wide_prime(stream_t * str)50 stream_wide_prime(stream_t *str)
51 {
52 stream_buffered_file_t *BF = &(str->s_type.BF);
53 wchar_t *current_position;
54 wchar_t *end_of_buffer;
55 wchar_t *next_nl;
56
57 ASSERT(!(str->s_status & STREAM_OUTPUT));
58 ASSERT(str->s_status & STREAM_OPEN);
59
60 if (str->s_status & STREAM_INSTANT && (str->s_buffer == NULL)) {
61 str->s_buffer = xzmap(0, WIDE_VBUF_SIZE, PROT_READ |
62 PROT_WRITE, MAP_PRIVATE, 0);
63 if (str->s_buffer == MAP_FAILED)
64 die(EMSG_MMAP);
65 str->s_buffer_size = WIDE_VBUF_SIZE;
66 }
67
68 ASSERT(str->s_buffer != NULL);
69
70 if (stream_is_primed(str)) {
71 int shelf_state = shelf;
72
73 ASSERT(str->s_current.l_data_length >= -1);
74 (void) memcpy(str->s_buffer, str->s_current.l_data.wp,
75 (str->s_current.l_data_length + 1) * sizeof (wchar_t));
76 str->s_current.l_data.wp = str->s_buffer;
77
78 if ((str->s_current.l_data_length == -1 ||
79 shelf_state == SHELF_OCCUPIED ||
80 *(str->s_current.l_data.wp +
81 str->s_current.l_data_length) != L'\0') &&
82 SOP_FETCH(str) == NEXT_LINE_INCOMPLETE &&
83 shelf_state == SHELF_OCCUPIED)
84 die(EMSG_MEMORY);
85
86 return (PRIME_SUCCEEDED);
87 }
88
89 stream_set(str, STREAM_PRIMED);
90
91 current_position = (wchar_t *)str->s_buffer;
92 /*LINTED ALIGNMENT*/
93 end_of_buffer = (wchar_t *)((char *)str->s_buffer +
94 str->s_buffer_size);
95
96 trip_eof(BF->s_fp);
97 if (!feof(BF->s_fp))
98 (void) fgetws(current_position, end_of_buffer
99 - current_position, BF->s_fp);
100 else {
101 stream_set(str, STREAM_EOS_REACHED);
102 stream_unset(str, STREAM_PRIMED);
103 return (PRIME_FAILED_EMPTY_FILE);
104 }
105
106 str->s_current.l_data.wp = current_position;
107 next_nl = xmemwchar(current_position, L'\n', end_of_buffer -
108 current_position);
109 if (next_nl == NULL) {
110 warn(WMSG_NEWLINE_ADDED, str->s_filename);
111 str->s_current.l_data_length = MIN(wslen(current_position),
112 end_of_buffer - current_position);
113 } else {
114 str->s_current.l_data_length = next_nl - current_position;
115 }
116 *(str->s_current.l_data.wp + str->s_current.l_data_length) = L'\0';
117
118 str->s_current.l_collate.wp = NULL;
119 str->s_current.l_collate_length = 0;
120
121 __S(stats_incr_fetches());
122 return (PRIME_SUCCEEDED);
123 }
124
125 static ssize_t
stream_wide_fetch(stream_t * str)126 stream_wide_fetch(stream_t *str)
127 {
128 ssize_t dist_to_buf_end;
129 int ret_val;
130 wchar_t *graft_pt;
131 wchar_t *next_nl;
132
133 ASSERT(str->s_status & STREAM_OPEN);
134 ASSERT((str->s_status & STREAM_EOS_REACHED) == 0);
135
136 graft_pt = str->s_current.l_data.wp + str->s_current.l_data_length + 1;
137
138 if (shelf == SHELF_VACANT)
139 str->s_current.l_data.wp = graft_pt;
140 else if (str->s_current.l_data_length > -1)
141 graft_pt--;
142
143 dist_to_buf_end = str->s_buffer_size / sizeof (wchar_t) - (graft_pt -
144 (wchar_t *)str->s_buffer);
145
146 if (dist_to_buf_end <= 1) {
147 str->s_current.l_data_length = -1;
148 return (NEXT_LINE_INCOMPLETE);
149 }
150
151 if (fgetws(graft_pt, dist_to_buf_end, str->s_type.BF.s_fp) == NULL) {
152 if (feof(str->s_type.BF.s_fp))
153 stream_set(str, STREAM_EOS_REACHED);
154 else
155 die(EMSG_READ, str->s_filename);
156 }
157
158 trip_eof(str->s_type.BF.s_fp);
159 if ((next_nl = xmemwchar(str->s_current.l_data.wp, L'\n',
160 dist_to_buf_end)) == NULL) {
161 str->s_current.l_data_length =
162 MIN(wslen(str->s_current.l_data.wp), dist_to_buf_end);
163 } else {
164 str->s_current.l_data_length = next_nl -
165 str->s_current.l_data.wp;
166 }
167
168 str->s_current.l_collate_length = 0;
169
170 if (*(str->s_current.l_data.wp + str->s_current.l_data_length) !=
171 L'\n') {
172 if (!feof(str->s_type.BF.s_fp)) {
173 if (shelf == SHELF_OCCUPIED)
174 die(EMSG_MEMORY);
175
176 shelf = SHELF_OCCUPIED;
177 ret_val = NEXT_LINE_INCOMPLETE;
178 __S(stats_incr_shelves());
179 } else {
180 stream_set(str, STREAM_EOS_REACHED);
181 warn(WMSG_NEWLINE_ADDED, str->s_filename);
182 }
183 } else {
184 shelf = SHELF_VACANT;
185 ret_val = NEXT_LINE_COMPLETE;
186 *(str->s_current.l_data.wp + str->s_current.l_data_length) =
187 L'\0';
188 __S(stats_incr_fetches());
189 }
190
191 return (ret_val);
192 }
193
194 ssize_t
stream_wide_fetch_overwrite(stream_t * str)195 stream_wide_fetch_overwrite(stream_t *str)
196 {
197 ssize_t dist_to_buf_end;
198
199 ASSERT(str->s_status & STREAM_OPEN);
200 ASSERT((str->s_status & STREAM_EOS_REACHED) == 0);
201
202 str->s_current.l_data.wp = str->s_buffer;
203 dist_to_buf_end = str->s_buffer_size / sizeof (wchar_t);
204
205 if (fgetws(str->s_current.l_data.wp, dist_to_buf_end,
206 str->s_type.BF.s_fp) == NULL) {
207 if (feof(str->s_type.BF.s_fp))
208 stream_set(str, STREAM_EOS_REACHED);
209 else
210 die(EMSG_READ, str->s_filename);
211 }
212
213 trip_eof(str->s_type.BF.s_fp);
214 str->s_current.l_data_length = wslen(str->s_current.l_data.wp) - 1;
215 str->s_current.l_collate_length = 0;
216
217 if (str->s_current.l_data_length == -1 ||
218 *(str->s_current.l_data.wp + str->s_current.l_data_length) !=
219 L'\n') {
220 if (!feof(str->s_type.BF.s_fp)) {
221 die(EMSG_MEMORY);
222 } else {
223 stream_set(str, STREAM_EOS_REACHED);
224 warn(WMSG_NEWLINE_ADDED, str->s_filename);
225 str->s_current.l_data_length++;
226 }
227 }
228
229 *(str->s_current.l_data.wp + str->s_current.l_data_length) = L'\0';
230
231 __S(stats_incr_fetches());
232 return (NEXT_LINE_COMPLETE);
233 }
234
235 static void
stream_wide_send_eol(stream_t * str)236 stream_wide_send_eol(stream_t *str)
237 {
238 wchar_t w_crlf[2] = { L'\n', L'\0' };
239
240 ASSERT(str->s_status & STREAM_OPEN);
241 ASSERT(str->s_status & STREAM_OUTPUT);
242
243 if (wxwrite(str->s_type.SF.s_fd, w_crlf) < 0)
244 die(EMSG_WRITE, str->s_filename);
245 }
246
247 static void
stream_wide_put_line(stream_t * str,line_rec_t * line)248 stream_wide_put_line(stream_t *str, line_rec_t *line)
249 {
250 ASSERT(str->s_status & STREAM_OPEN);
251 ASSERT(str->s_status & STREAM_OUTPUT);
252
253 if (line->l_data_length >= 0) {
254 if (wxwrite(str->s_type.SF.s_fd, line->l_data.wp) >= 0) {
255 stream_wide_send_eol(str);
256 __S(stats_incr_puts());
257 } else
258 die(EMSG_WRITE, str->s_filename);
259 }
260 safe_free(line->l_raw_collate.wp);
261 line->l_raw_collate.wp = NULL;
262 }
263
264 void
stream_wide_put_line_unique(stream_t * str,line_rec_t * line)265 stream_wide_put_line_unique(stream_t *str, line_rec_t *line)
266 {
267 static line_rec_t pvs;
268 static size_t collate_buf_len;
269
270 ASSERT(str->s_status & STREAM_OPEN);
271 ASSERT(str->s_status & STREAM_OUTPUT);
272
273 if ((pvs.l_collate.sp == NULL ||
274 collated_wide(&pvs, line, 0, COLL_UNIQUE) != 0) &&
275 line->l_data_length >= 0) {
276 stream_wide_put_line(str, line);
277
278 if (line->l_collate_length + sizeof (wchar_t) >
279 collate_buf_len) {
280 pvs.l_collate.sp = safe_realloc(pvs.l_collate.sp,
281 line->l_collate_length + sizeof (wchar_t));
282 collate_buf_len = line->l_collate_length +
283 sizeof (wchar_t);
284 }
285
286 (void) memcpy(pvs.l_collate.sp, line->l_collate.sp,
287 line->l_collate_length);
288 /* LINTED ALIGNMENT */
289 *(wchar_t *)(pvs.l_collate.sp + line->l_collate_length) = L'\0';
290 pvs.l_collate_length = line->l_collate_length;
291 }
292 }
293
294 static int
stream_wide_eos(stream_t * str)295 stream_wide_eos(stream_t *str)
296 {
297 int retval = 0;
298
299 if (str == NULL || str->s_status & STREAM_EOS_REACHED)
300 return (1);
301
302 trip_eof(str->s_type.BF.s_fp);
303 if (feof(str->s_type.BF.s_fp) &&
304 shelf == SHELF_VACANT &&
305 str->s_current.l_collate_length != -1) {
306 retval = 1;
307 stream_set(str, STREAM_EOS_REACHED);
308 }
309
310 return (retval);
311 }
312
313 /*ARGSUSED*/
314 static void
stream_wide_release_line(stream_t * str)315 stream_wide_release_line(stream_t *str)
316 {
317 }
318
319 const stream_ops_t stream_wide_ops = {
320 stream_stdio_is_closable,
321 stream_stdio_close,
322 stream_wide_eos,
323 stream_wide_fetch,
324 stream_stdio_flush,
325 stream_stdio_free,
326 stream_stdio_open_for_write,
327 stream_wide_prime,
328 stream_wide_put_line,
329 stream_wide_release_line,
330 stream_wide_send_eol,
331 stream_stdio_unlink
332 };
333