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