1 | // File based streams -*- C++ -*- |
2 | |
3 | // Copyright (C) 1997-2018 Free Software Foundation, Inc. |
4 | // |
5 | // This file is part of the GNU ISO C++ Library. This library is free |
6 | // software; you can redistribute it and/or modify it under the |
7 | // terms of the GNU General Public License as published by the |
8 | // Free Software Foundation; either version 3, or (at your option) |
9 | // any later version. |
10 | |
11 | // This library is distributed in the hope that it will be useful, |
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | // GNU General Public License for more details. |
15 | |
16 | // Under Section 7 of GPL version 3, you are granted additional |
17 | // permissions described in the GCC Runtime Library Exception, version |
18 | // 3.1, as published by the Free Software Foundation. |
19 | |
20 | // You should have received a copy of the GNU General Public License and |
21 | // a copy of the GCC Runtime Library Exception along with this program; |
22 | // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
23 | // <http://www.gnu.org/licenses/>. |
24 | |
25 | /** @file bits/fstream.tcc |
26 | * This is an internal header file, included by other library headers. |
27 | * Do not attempt to use it directly. @headername{fstream} |
28 | */ |
29 | |
30 | // |
31 | // ISO C++ 14882: 27.8 File-based streams |
32 | // |
33 | |
34 | #ifndef _FSTREAM_TCC |
35 | #define _FSTREAM_TCC 1 |
36 | |
37 | #pragma GCC system_header |
38 | |
39 | #include <bits/cxxabi_forced.h> |
40 | #include <bits/move.h> // for swap |
41 | |
42 | namespace std _GLIBCXX_VISIBILITY(default) |
43 | { |
44 | _GLIBCXX_BEGIN_NAMESPACE_VERSION |
45 | |
46 | template<typename _CharT, typename _Traits> |
47 | void |
48 | basic_filebuf<_CharT, _Traits>:: |
49 | _M_allocate_internal_buffer() |
50 | { |
51 | // Allocate internal buffer only if one doesn't already exist |
52 | // (either allocated or provided by the user via setbuf). |
53 | if (!_M_buf_allocated && !_M_buf) |
54 | { |
55 | _M_buf = new char_type[_M_buf_size]; |
56 | _M_buf_allocated = true; |
57 | } |
58 | } |
59 | |
60 | template<typename _CharT, typename _Traits> |
61 | void |
62 | basic_filebuf<_CharT, _Traits>:: |
63 | _M_destroy_internal_buffer() throw() |
64 | { |
65 | if (_M_buf_allocated) |
66 | { |
67 | delete [] _M_buf; |
68 | _M_buf = 0; |
69 | _M_buf_allocated = false; |
70 | } |
71 | delete [] _M_ext_buf; |
72 | _M_ext_buf = 0; |
73 | _M_ext_buf_size = 0; |
74 | _M_ext_next = 0; |
75 | _M_ext_end = 0; |
76 | } |
77 | |
78 | template<typename _CharT, typename _Traits> |
79 | basic_filebuf<_CharT, _Traits>:: |
80 | basic_filebuf() : __streambuf_type(), _M_lock(), _M_file(&_M_lock), |
81 | _M_mode(ios_base::openmode(0)), _M_state_beg(), _M_state_cur(), |
82 | _M_state_last(), _M_buf(0), _M_buf_size(BUFSIZ), |
83 | _M_buf_allocated(false), _M_reading(false), _M_writing(false), _M_pback(), |
84 | _M_pback_cur_save(0), _M_pback_end_save(0), _M_pback_init(false), |
85 | _M_codecvt(0), _M_ext_buf(0), _M_ext_buf_size(0), _M_ext_next(0), |
86 | _M_ext_end(0) |
87 | { |
88 | if (has_facet<__codecvt_type>(this->_M_buf_locale)) |
89 | _M_codecvt = &use_facet<__codecvt_type>(this->_M_buf_locale); |
90 | } |
91 | |
92 | #if __cplusplus >= 201103L |
93 | template<typename _CharT, typename _Traits> |
94 | basic_filebuf<_CharT, _Traits>:: |
95 | basic_filebuf(basic_filebuf&& __rhs) |
96 | : __streambuf_type(__rhs), |
97 | _M_lock(), _M_file(std::move(__rhs._M_file), &_M_lock), |
98 | _M_mode(std::__exchange(__rhs._M_mode, ios_base::openmode(0))), |
99 | _M_state_beg(std::move(__rhs._M_state_beg)), |
100 | _M_state_cur(std::move(__rhs._M_state_cur)), |
101 | _M_state_last(std::move(__rhs._M_state_last)), |
102 | _M_buf(std::__exchange(__rhs._M_buf, nullptr)), |
103 | _M_buf_size(std::__exchange(__rhs._M_buf_size, 1)), |
104 | _M_buf_allocated(std::__exchange(__rhs._M_buf_allocated, false)), |
105 | _M_reading(std::__exchange(__rhs._M_reading, false)), |
106 | _M_writing(std::__exchange(__rhs._M_writing, false)), |
107 | _M_pback(__rhs._M_pback), |
108 | _M_pback_cur_save(std::__exchange(__rhs._M_pback_cur_save, nullptr)), |
109 | _M_pback_end_save(std::__exchange(__rhs._M_pback_end_save, nullptr)), |
110 | _M_pback_init(std::__exchange(__rhs._M_pback_init, false)), |
111 | _M_codecvt(__rhs._M_codecvt), |
112 | _M_ext_buf(std::__exchange(__rhs._M_ext_buf, nullptr)), |
113 | _M_ext_buf_size(std::__exchange(__rhs._M_ext_buf_size, 0)), |
114 | _M_ext_next(std::__exchange(__rhs._M_ext_next, nullptr)), |
115 | _M_ext_end(std::__exchange(__rhs._M_ext_end, nullptr)) |
116 | { |
117 | __rhs._M_set_buffer(-1); |
118 | __rhs._M_state_last = __rhs._M_state_cur = __rhs._M_state_beg; |
119 | } |
120 | |
121 | template<typename _CharT, typename _Traits> |
122 | basic_filebuf<_CharT, _Traits>& |
123 | basic_filebuf<_CharT, _Traits>:: |
124 | operator=(basic_filebuf&& __rhs) |
125 | { |
126 | this->close(); |
127 | __streambuf_type::operator=(__rhs); |
128 | _M_file.swap(__rhs._M_file); |
129 | _M_mode = std::__exchange(__rhs._M_mode, ios_base::openmode(0)); |
130 | _M_state_beg = std::move(__rhs._M_state_beg); |
131 | _M_state_cur = std::move(__rhs._M_state_cur); |
132 | _M_state_last = std::move(__rhs._M_state_last); |
133 | _M_buf = std::__exchange(__rhs._M_buf, nullptr); |
134 | _M_buf_size = std::__exchange(__rhs._M_buf_size, 1); |
135 | _M_buf_allocated = std::__exchange(__rhs._M_buf_allocated, false); |
136 | _M_ext_buf = std::__exchange(__rhs._M_ext_buf, nullptr); |
137 | _M_ext_buf_size = std::__exchange(__rhs._M_ext_buf_size, 0); |
138 | _M_ext_next = std::__exchange(__rhs._M_ext_next, nullptr); |
139 | _M_ext_end = std::__exchange(__rhs._M_ext_end, nullptr); |
140 | _M_reading = std::__exchange(__rhs._M_reading, false); |
141 | _M_writing = std::__exchange(__rhs._M_writing, false); |
142 | _M_pback_cur_save = std::__exchange(__rhs._M_pback_cur_save, nullptr); |
143 | _M_pback_end_save = std::__exchange(__rhs._M_pback_end_save, nullptr); |
144 | _M_pback_init = std::__exchange(__rhs._M_pback_init, false); |
145 | __rhs._M_set_buffer(-1); |
146 | __rhs._M_state_last = __rhs._M_state_cur = __rhs._M_state_beg; |
147 | return *this; |
148 | } |
149 | |
150 | template<typename _CharT, typename _Traits> |
151 | void |
152 | basic_filebuf<_CharT, _Traits>:: |
153 | swap(basic_filebuf& __rhs) |
154 | { |
155 | __streambuf_type::swap(__rhs); |
156 | _M_file.swap(__rhs._M_file); |
157 | std::swap(_M_mode, __rhs._M_mode); |
158 | std::swap(_M_state_beg, __rhs._M_state_beg); |
159 | std::swap(_M_state_cur, __rhs._M_state_cur); |
160 | std::swap(_M_state_last, __rhs._M_state_last); |
161 | std::swap(_M_buf, __rhs._M_buf); |
162 | std::swap(_M_buf_size, __rhs._M_buf_size); |
163 | std::swap(_M_buf_allocated, __rhs._M_buf_allocated); |
164 | std::swap(_M_ext_buf, __rhs._M_ext_buf); |
165 | std::swap(_M_ext_buf_size, __rhs._M_ext_buf_size); |
166 | std::swap(_M_ext_next, __rhs._M_ext_next); |
167 | std::swap(_M_ext_end, __rhs._M_ext_end); |
168 | std::swap(_M_reading, __rhs._M_reading); |
169 | std::swap(_M_writing, __rhs._M_writing); |
170 | std::swap(_M_pback_cur_save, __rhs._M_pback_cur_save); |
171 | std::swap(_M_pback_end_save, __rhs._M_pback_end_save); |
172 | std::swap(_M_pback_init, __rhs._M_pback_init); |
173 | } |
174 | #endif |
175 | |
176 | template<typename _CharT, typename _Traits> |
177 | typename basic_filebuf<_CharT, _Traits>::__filebuf_type* |
178 | basic_filebuf<_CharT, _Traits>:: |
179 | open(const char* __s, ios_base::openmode __mode) |
180 | { |
181 | __filebuf_type *__ret = 0; |
182 | if (!this->is_open()) |
183 | { |
184 | _M_file.open(__s, __mode); |
185 | if (this->is_open()) |
186 | { |
187 | _M_allocate_internal_buffer(); |
188 | _M_mode = __mode; |
189 | |
190 | // Setup initial buffer to 'uncommitted' mode. |
191 | _M_reading = false; |
192 | _M_writing = false; |
193 | _M_set_buffer(-1); |
194 | |
195 | // Reset to initial state. |
196 | _M_state_last = _M_state_cur = _M_state_beg; |
197 | |
198 | // 27.8.1.3,4 |
199 | if ((__mode & ios_base::ate) |
200 | && this->seekoff(0, ios_base::end, __mode) |
201 | == pos_type(off_type(-1))) |
202 | this->close(); |
203 | else |
204 | __ret = this; |
205 | } |
206 | } |
207 | return __ret; |
208 | } |
209 | |
210 | template<typename _CharT, typename _Traits> |
211 | typename basic_filebuf<_CharT, _Traits>::__filebuf_type* |
212 | basic_filebuf<_CharT, _Traits>:: |
213 | close() |
214 | { |
215 | if (!this->is_open()) |
216 | return 0; |
217 | |
218 | bool __testfail = false; |
219 | { |
220 | // NB: Do this here so that re-opened filebufs will be cool... |
221 | struct __close_sentry |
222 | { |
223 | basic_filebuf *__fb; |
224 | __close_sentry (basic_filebuf *__fbi): __fb(__fbi) { } |
225 | ~__close_sentry () |
226 | { |
227 | __fb->_M_mode = ios_base::openmode(0); |
228 | __fb->_M_pback_init = false; |
229 | __fb->_M_destroy_internal_buffer(); |
230 | __fb->_M_reading = false; |
231 | __fb->_M_writing = false; |
232 | __fb->_M_set_buffer(-1); |
233 | __fb->_M_state_last = __fb->_M_state_cur = __fb->_M_state_beg; |
234 | } |
235 | } __cs (this); |
236 | |
237 | __try |
238 | { |
239 | if (!_M_terminate_output()) |
240 | __testfail = true; |
241 | } |
242 | __catch(__cxxabiv1::__forced_unwind&) |
243 | { |
244 | _M_file.close(); |
245 | __throw_exception_again; |
246 | } |
247 | __catch(...) |
248 | { __testfail = true; } |
249 | } |
250 | |
251 | if (!_M_file.close()) |
252 | __testfail = true; |
253 | |
254 | if (__testfail) |
255 | return 0; |
256 | else |
257 | return this; |
258 | } |
259 | |
260 | template<typename _CharT, typename _Traits> |
261 | streamsize |
262 | basic_filebuf<_CharT, _Traits>:: |
263 | showmanyc() |
264 | { |
265 | streamsize __ret = -1; |
266 | const bool __testin = _M_mode & ios_base::in; |
267 | if (__testin && this->is_open()) |
268 | { |
269 | // For a stateful encoding (-1) the pending sequence might be just |
270 | // shift and unshift prefixes with no actual character. |
271 | __ret = this->egptr() - this->gptr(); |
272 | |
273 | #if _GLIBCXX_HAVE_DOS_BASED_FILESYSTEM |
274 | // About this workaround, see libstdc++/20806. |
275 | const bool __testbinary = _M_mode & ios_base::binary; |
276 | if (__check_facet(_M_codecvt).encoding() >= 0 |
277 | && __testbinary) |
278 | #else |
279 | if (__check_facet(_M_codecvt).encoding() >= 0) |
280 | #endif |
281 | __ret += _M_file.showmanyc() / _M_codecvt->max_length(); |
282 | } |
283 | return __ret; |
284 | } |
285 | |
286 | template<typename _CharT, typename _Traits> |
287 | typename basic_filebuf<_CharT, _Traits>::int_type |
288 | basic_filebuf<_CharT, _Traits>:: |
289 | underflow() |
290 | { |
291 | int_type __ret = traits_type::eof(); |
292 | const bool __testin = _M_mode & ios_base::in; |
293 | if (__testin) |
294 | { |
295 | if (_M_writing) |
296 | { |
297 | if (overflow() == traits_type::eof()) |
298 | return __ret; |
299 | _M_set_buffer(-1); |
300 | _M_writing = false; |
301 | } |
302 | // Check for pback madness, and if so switch back to the |
303 | // normal buffers and jet outta here before expensive |
304 | // fileops happen... |
305 | _M_destroy_pback(); |
306 | |
307 | if (this->gptr() < this->egptr()) |
308 | return traits_type::to_int_type(*this->gptr()); |
309 | |
310 | // Get and convert input sequence. |
311 | const size_t __buflen = _M_buf_size > 1 ? _M_buf_size - 1 : 1; |
312 | |
313 | // Will be set to true if ::read() returns 0 indicating EOF. |
314 | bool __got_eof = false; |
315 | // Number of internal characters produced. |
316 | streamsize __ilen = 0; |
317 | codecvt_base::result __r = codecvt_base::ok; |
318 | if (__check_facet(_M_codecvt).always_noconv()) |
319 | { |
320 | __ilen = _M_file.xsgetn(reinterpret_cast<char*>(this->eback()), |
321 | __buflen); |
322 | if (__ilen == 0) |
323 | __got_eof = true; |
324 | } |
325 | else |
326 | { |
327 | // Worst-case number of external bytes. |
328 | // XXX Not done encoding() == -1. |
329 | const int __enc = _M_codecvt->encoding(); |
330 | streamsize __blen; // Minimum buffer size. |
331 | streamsize __rlen; // Number of chars to read. |
332 | if (__enc > 0) |
333 | __blen = __rlen = __buflen * __enc; |
334 | else |
335 | { |
336 | __blen = __buflen + _M_codecvt->max_length() - 1; |
337 | __rlen = __buflen; |
338 | } |
339 | const streamsize __remainder = _M_ext_end - _M_ext_next; |
340 | __rlen = __rlen > __remainder ? __rlen - __remainder : 0; |
341 | |
342 | // An imbue in 'read' mode implies first converting the external |
343 | // chars already present. |
344 | if (_M_reading && this->egptr() == this->eback() && __remainder) |
345 | __rlen = 0; |
346 | |
347 | // Allocate buffer if necessary and move unconverted |
348 | // bytes to front. |
349 | if (_M_ext_buf_size < __blen) |
350 | { |
351 | char* __buf = new char[__blen]; |
352 | if (__remainder) |
353 | __builtin_memcpy(__buf, _M_ext_next, __remainder); |
354 | |
355 | delete [] _M_ext_buf; |
356 | _M_ext_buf = __buf; |
357 | _M_ext_buf_size = __blen; |
358 | } |
359 | else if (__remainder) |
360 | __builtin_memmove(_M_ext_buf, _M_ext_next, __remainder); |
361 | |
362 | _M_ext_next = _M_ext_buf; |
363 | _M_ext_end = _M_ext_buf + __remainder; |
364 | _M_state_last = _M_state_cur; |
365 | |
366 | do |
367 | { |
368 | if (__rlen > 0) |
369 | { |
370 | // Sanity check! |
371 | // This may fail if the return value of |
372 | // codecvt::max_length() is bogus. |
373 | if (_M_ext_end - _M_ext_buf + __rlen > _M_ext_buf_size) |
374 | { |
375 | __throw_ios_failure(__N("basic_filebuf::underflow " |
376 | "codecvt::max_length() " |
377 | "is not valid" )); |
378 | } |
379 | streamsize __elen = _M_file.xsgetn(_M_ext_end, __rlen); |
380 | if (__elen == 0) |
381 | __got_eof = true; |
382 | else if (__elen == -1) |
383 | break; |
384 | _M_ext_end += __elen; |
385 | } |
386 | |
387 | char_type* __iend = this->eback(); |
388 | if (_M_ext_next < _M_ext_end) |
389 | __r = _M_codecvt->in(_M_state_cur, _M_ext_next, |
390 | _M_ext_end, _M_ext_next, |
391 | this->eback(), |
392 | this->eback() + __buflen, __iend); |
393 | if (__r == codecvt_base::noconv) |
394 | { |
395 | size_t __avail = _M_ext_end - _M_ext_buf; |
396 | __ilen = std::min(__avail, __buflen); |
397 | traits_type::copy(this->eback(), |
398 | reinterpret_cast<char_type*> |
399 | (_M_ext_buf), __ilen); |
400 | _M_ext_next = _M_ext_buf + __ilen; |
401 | } |
402 | else |
403 | __ilen = __iend - this->eback(); |
404 | |
405 | // _M_codecvt->in may return error while __ilen > 0: this is |
406 | // ok, and actually occurs in case of mixed encodings (e.g., |
407 | // XML files). |
408 | if (__r == codecvt_base::error) |
409 | break; |
410 | |
411 | __rlen = 1; |
412 | } |
413 | while (__ilen == 0 && !__got_eof); |
414 | } |
415 | |
416 | if (__ilen > 0) |
417 | { |
418 | _M_set_buffer(__ilen); |
419 | _M_reading = true; |
420 | __ret = traits_type::to_int_type(*this->gptr()); |
421 | } |
422 | else if (__got_eof) |
423 | { |
424 | // If the actual end of file is reached, set 'uncommitted' |
425 | // mode, thus allowing an immediate write without an |
426 | // intervening seek. |
427 | _M_set_buffer(-1); |
428 | _M_reading = false; |
429 | // However, reaching it while looping on partial means that |
430 | // the file has got an incomplete character. |
431 | if (__r == codecvt_base::partial) |
432 | __throw_ios_failure(__N("basic_filebuf::underflow " |
433 | "incomplete character in file" )); |
434 | } |
435 | else if (__r == codecvt_base::error) |
436 | __throw_ios_failure(__N("basic_filebuf::underflow " |
437 | "invalid byte sequence in file" )); |
438 | else |
439 | __throw_ios_failure(__N("basic_filebuf::underflow " |
440 | "error reading the file" )); |
441 | } |
442 | return __ret; |
443 | } |
444 | |
445 | template<typename _CharT, typename _Traits> |
446 | typename basic_filebuf<_CharT, _Traits>::int_type |
447 | basic_filebuf<_CharT, _Traits>:: |
448 | pbackfail(int_type __i) |
449 | { |
450 | int_type __ret = traits_type::eof(); |
451 | const bool __testin = _M_mode & ios_base::in; |
452 | if (__testin) |
453 | { |
454 | if (_M_writing) |
455 | { |
456 | if (overflow() == traits_type::eof()) |
457 | return __ret; |
458 | _M_set_buffer(-1); |
459 | _M_writing = false; |
460 | } |
461 | // Remember whether the pback buffer is active, otherwise below |
462 | // we may try to store in it a second char (libstdc++/9761). |
463 | const bool __testpb = _M_pback_init; |
464 | const bool __testeof = traits_type::eq_int_type(__i, __ret); |
465 | int_type __tmp; |
466 | if (this->eback() < this->gptr()) |
467 | { |
468 | this->gbump(-1); |
469 | __tmp = traits_type::to_int_type(*this->gptr()); |
470 | } |
471 | else if (this->seekoff(-1, ios_base::cur) != pos_type(off_type(-1))) |
472 | { |
473 | __tmp = this->underflow(); |
474 | if (traits_type::eq_int_type(__tmp, __ret)) |
475 | return __ret; |
476 | } |
477 | else |
478 | { |
479 | // At the beginning of the buffer, need to make a |
480 | // putback position available. But the seek may fail |
481 | // (f.i., at the beginning of a file, see |
482 | // libstdc++/9439) and in that case we return |
483 | // traits_type::eof(). |
484 | return __ret; |
485 | } |
486 | |
487 | // Try to put back __i into input sequence in one of three ways. |
488 | // Order these tests done in is unspecified by the standard. |
489 | if (!__testeof && traits_type::eq_int_type(__i, __tmp)) |
490 | __ret = __i; |
491 | else if (__testeof) |
492 | __ret = traits_type::not_eof(__i); |
493 | else if (!__testpb) |
494 | { |
495 | _M_create_pback(); |
496 | _M_reading = true; |
497 | *this->gptr() = traits_type::to_char_type(__i); |
498 | __ret = __i; |
499 | } |
500 | } |
501 | return __ret; |
502 | } |
503 | |
504 | template<typename _CharT, typename _Traits> |
505 | typename basic_filebuf<_CharT, _Traits>::int_type |
506 | basic_filebuf<_CharT, _Traits>:: |
507 | overflow(int_type __c) |
508 | { |
509 | int_type __ret = traits_type::eof(); |
510 | const bool __testeof = traits_type::eq_int_type(__c, __ret); |
511 | const bool __testout = (_M_mode & ios_base::out |
512 | || _M_mode & ios_base::app); |
513 | if (__testout) |
514 | { |
515 | if (_M_reading) |
516 | { |
517 | _M_destroy_pback(); |
518 | const int __gptr_off = _M_get_ext_pos(_M_state_last); |
519 | if (_M_seek(__gptr_off, ios_base::cur, _M_state_last) |
520 | == pos_type(off_type(-1))) |
521 | return __ret; |
522 | } |
523 | if (this->pbase() < this->pptr()) |
524 | { |
525 | // If appropriate, append the overflow char. |
526 | if (!__testeof) |
527 | { |
528 | *this->pptr() = traits_type::to_char_type(__c); |
529 | this->pbump(1); |
530 | } |
531 | |
532 | // Convert pending sequence to external representation, |
533 | // and output. |
534 | if (_M_convert_to_external(this->pbase(), |
535 | this->pptr() - this->pbase())) |
536 | { |
537 | _M_set_buffer(0); |
538 | __ret = traits_type::not_eof(__c); |
539 | } |
540 | } |
541 | else if (_M_buf_size > 1) |
542 | { |
543 | // Overflow in 'uncommitted' mode: set _M_writing, set |
544 | // the buffer to the initial 'write' mode, and put __c |
545 | // into the buffer. |
546 | _M_set_buffer(0); |
547 | _M_writing = true; |
548 | if (!__testeof) |
549 | { |
550 | *this->pptr() = traits_type::to_char_type(__c); |
551 | this->pbump(1); |
552 | } |
553 | __ret = traits_type::not_eof(__c); |
554 | } |
555 | else |
556 | { |
557 | // Unbuffered. |
558 | char_type __conv = traits_type::to_char_type(__c); |
559 | if (__testeof || _M_convert_to_external(&__conv, 1)) |
560 | { |
561 | _M_writing = true; |
562 | __ret = traits_type::not_eof(__c); |
563 | } |
564 | } |
565 | } |
566 | return __ret; |
567 | } |
568 | |
569 | template<typename _CharT, typename _Traits> |
570 | bool |
571 | basic_filebuf<_CharT, _Traits>:: |
572 | _M_convert_to_external(_CharT* __ibuf, streamsize __ilen) |
573 | { |
574 | // Sizes of external and pending output. |
575 | streamsize __elen; |
576 | streamsize __plen; |
577 | if (__check_facet(_M_codecvt).always_noconv()) |
578 | { |
579 | __elen = _M_file.xsputn(reinterpret_cast<char*>(__ibuf), __ilen); |
580 | __plen = __ilen; |
581 | } |
582 | else |
583 | { |
584 | // Worst-case number of external bytes needed. |
585 | // XXX Not done encoding() == -1. |
586 | streamsize __blen = __ilen * _M_codecvt->max_length(); |
587 | char* __buf = static_cast<char*>(__builtin_alloca(__blen)); |
588 | |
589 | char* __bend; |
590 | const char_type* __iend; |
591 | codecvt_base::result __r; |
592 | __r = _M_codecvt->out(_M_state_cur, __ibuf, __ibuf + __ilen, |
593 | __iend, __buf, __buf + __blen, __bend); |
594 | |
595 | if (__r == codecvt_base::ok || __r == codecvt_base::partial) |
596 | __blen = __bend - __buf; |
597 | else if (__r == codecvt_base::noconv) |
598 | { |
599 | // Same as the always_noconv case above. |
600 | __buf = reinterpret_cast<char*>(__ibuf); |
601 | __blen = __ilen; |
602 | } |
603 | else |
604 | __throw_ios_failure(__N("basic_filebuf::_M_convert_to_external " |
605 | "conversion error" )); |
606 | |
607 | __elen = _M_file.xsputn(__buf, __blen); |
608 | __plen = __blen; |
609 | |
610 | // Try once more for partial conversions. |
611 | if (__r == codecvt_base::partial && __elen == __plen) |
612 | { |
613 | const char_type* __iresume = __iend; |
614 | streamsize __rlen = this->pptr() - __iend; |
615 | __r = _M_codecvt->out(_M_state_cur, __iresume, |
616 | __iresume + __rlen, __iend, __buf, |
617 | __buf + __blen, __bend); |
618 | if (__r != codecvt_base::error) |
619 | { |
620 | __rlen = __bend - __buf; |
621 | __elen = _M_file.xsputn(__buf, __rlen); |
622 | __plen = __rlen; |
623 | } |
624 | else |
625 | __throw_ios_failure(__N("basic_filebuf::_M_convert_to_external " |
626 | "conversion error" )); |
627 | } |
628 | } |
629 | return __elen == __plen; |
630 | } |
631 | |
632 | template<typename _CharT, typename _Traits> |
633 | streamsize |
634 | basic_filebuf<_CharT, _Traits>:: |
635 | xsgetn(_CharT* __s, streamsize __n) |
636 | { |
637 | // Clear out pback buffer before going on to the real deal... |
638 | streamsize __ret = 0; |
639 | if (_M_pback_init) |
640 | { |
641 | if (__n > 0 && this->gptr() == this->eback()) |
642 | { |
643 | *__s++ = *this->gptr(); // emulate non-underflowing sbumpc |
644 | this->gbump(1); |
645 | __ret = 1; |
646 | --__n; |
647 | } |
648 | _M_destroy_pback(); |
649 | } |
650 | else if (_M_writing) |
651 | { |
652 | if (overflow() == traits_type::eof()) |
653 | return __ret; |
654 | _M_set_buffer(-1); |
655 | _M_writing = false; |
656 | } |
657 | |
658 | // Optimization in the always_noconv() case, to be generalized in the |
659 | // future: when __n > __buflen we read directly instead of using the |
660 | // buffer repeatedly. |
661 | const bool __testin = _M_mode & ios_base::in; |
662 | const streamsize __buflen = _M_buf_size > 1 ? _M_buf_size - 1 : 1; |
663 | |
664 | if (__n > __buflen && __check_facet(_M_codecvt).always_noconv() |
665 | && __testin) |
666 | { |
667 | // First, copy the chars already present in the buffer. |
668 | const streamsize __avail = this->egptr() - this->gptr(); |
669 | if (__avail != 0) |
670 | { |
671 | traits_type::copy(__s, this->gptr(), __avail); |
672 | __s += __avail; |
673 | this->setg(this->eback(), this->gptr() + __avail, this->egptr()); |
674 | __ret += __avail; |
675 | __n -= __avail; |
676 | } |
677 | |
678 | // Need to loop in case of short reads (relatively common |
679 | // with pipes). |
680 | streamsize __len; |
681 | for (;;) |
682 | { |
683 | __len = _M_file.xsgetn(reinterpret_cast<char*>(__s), __n); |
684 | if (__len == -1) |
685 | __throw_ios_failure(__N("basic_filebuf::xsgetn " |
686 | "error reading the file" )); |
687 | if (__len == 0) |
688 | break; |
689 | |
690 | __n -= __len; |
691 | __ret += __len; |
692 | if (__n == 0) |
693 | break; |
694 | |
695 | __s += __len; |
696 | } |
697 | |
698 | if (__n == 0) |
699 | { |
700 | // Set _M_reading. Buffer is already in initial 'read' mode. |
701 | _M_reading = true; |
702 | } |
703 | else if (__len == 0) |
704 | { |
705 | // If end of file is reached, set 'uncommitted' |
706 | // mode, thus allowing an immediate write without |
707 | // an intervening seek. |
708 | _M_set_buffer(-1); |
709 | _M_reading = false; |
710 | } |
711 | } |
712 | else |
713 | __ret += __streambuf_type::xsgetn(__s, __n); |
714 | |
715 | return __ret; |
716 | } |
717 | |
718 | template<typename _CharT, typename _Traits> |
719 | streamsize |
720 | basic_filebuf<_CharT, _Traits>:: |
721 | xsputn(const _CharT* __s, streamsize __n) |
722 | { |
723 | streamsize __ret = 0; |
724 | // Optimization in the always_noconv() case, to be generalized in the |
725 | // future: when __n is sufficiently large we write directly instead of |
726 | // using the buffer. |
727 | const bool __testout = (_M_mode & ios_base::out |
728 | || _M_mode & ios_base::app); |
729 | if (__check_facet(_M_codecvt).always_noconv() |
730 | && __testout && !_M_reading) |
731 | { |
732 | // Measurement would reveal the best choice. |
733 | const streamsize __chunk = 1ul << 10; |
734 | streamsize __bufavail = this->epptr() - this->pptr(); |
735 | |
736 | // Don't mistake 'uncommitted' mode buffered with unbuffered. |
737 | if (!_M_writing && _M_buf_size > 1) |
738 | __bufavail = _M_buf_size - 1; |
739 | |
740 | const streamsize __limit = std::min(__chunk, __bufavail); |
741 | if (__n >= __limit) |
742 | { |
743 | const streamsize __buffill = this->pptr() - this->pbase(); |
744 | const char* __buf = reinterpret_cast<const char*>(this->pbase()); |
745 | __ret = _M_file.xsputn_2(__buf, __buffill, |
746 | reinterpret_cast<const char*>(__s), |
747 | __n); |
748 | if (__ret == __buffill + __n) |
749 | { |
750 | _M_set_buffer(0); |
751 | _M_writing = true; |
752 | } |
753 | if (__ret > __buffill) |
754 | __ret -= __buffill; |
755 | else |
756 | __ret = 0; |
757 | } |
758 | else |
759 | __ret = __streambuf_type::xsputn(__s, __n); |
760 | } |
761 | else |
762 | __ret = __streambuf_type::xsputn(__s, __n); |
763 | return __ret; |
764 | } |
765 | |
766 | template<typename _CharT, typename _Traits> |
767 | typename basic_filebuf<_CharT, _Traits>::__streambuf_type* |
768 | basic_filebuf<_CharT, _Traits>:: |
769 | setbuf(char_type* __s, streamsize __n) |
770 | { |
771 | if (!this->is_open()) |
772 | { |
773 | if (__s == 0 && __n == 0) |
774 | _M_buf_size = 1; |
775 | else if (__s && __n > 0) |
776 | { |
777 | // This is implementation-defined behavior, and assumes that |
778 | // an external char_type array of length __n exists and has |
779 | // been pre-allocated. If this is not the case, things will |
780 | // quickly blow up. When __n > 1, __n - 1 positions will be |
781 | // used for the get area, __n - 1 for the put area and 1 |
782 | // position to host the overflow char of a full put area. |
783 | // When __n == 1, 1 position will be used for the get area |
784 | // and 0 for the put area, as in the unbuffered case above. |
785 | _M_buf = __s; |
786 | _M_buf_size = __n; |
787 | } |
788 | } |
789 | return this; |
790 | } |
791 | |
792 | |
793 | // According to 27.8.1.4 p11 - 13, seekoff should ignore the last |
794 | // argument (of type openmode). |
795 | template<typename _CharT, typename _Traits> |
796 | typename basic_filebuf<_CharT, _Traits>::pos_type |
797 | basic_filebuf<_CharT, _Traits>:: |
798 | seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode) |
799 | { |
800 | int __width = 0; |
801 | if (_M_codecvt) |
802 | __width = _M_codecvt->encoding(); |
803 | if (__width < 0) |
804 | __width = 0; |
805 | |
806 | pos_type __ret = pos_type(off_type(-1)); |
807 | const bool __testfail = __off != 0 && __width <= 0; |
808 | if (this->is_open() && !__testfail) |
809 | { |
810 | // tellg and tellp queries do not affect any state, unless |
811 | // ! always_noconv and the put sequence is not empty. |
812 | // In that case, determining the position requires converting the |
813 | // put sequence. That doesn't use ext_buf, so requires a flush. |
814 | bool __no_movement = __way == ios_base::cur && __off == 0 |
815 | && (!_M_writing || _M_codecvt->always_noconv()); |
816 | |
817 | // Ditch any pback buffers to avoid confusion. |
818 | if (!__no_movement) |
819 | _M_destroy_pback(); |
820 | |
821 | // Correct state at destination. Note that this is the correct |
822 | // state for the current position during output, because |
823 | // codecvt::unshift() returns the state to the initial state. |
824 | // This is also the correct state at the end of the file because |
825 | // an unshift sequence should have been written at the end. |
826 | __state_type __state = _M_state_beg; |
827 | off_type __computed_off = __off * __width; |
828 | if (_M_reading && __way == ios_base::cur) |
829 | { |
830 | __state = _M_state_last; |
831 | __computed_off += _M_get_ext_pos(__state); |
832 | } |
833 | if (!__no_movement) |
834 | __ret = _M_seek(__computed_off, __way, __state); |
835 | else |
836 | { |
837 | if (_M_writing) |
838 | __computed_off = this->pptr() - this->pbase(); |
839 | |
840 | off_type __file_off = _M_file.seekoff(0, ios_base::cur); |
841 | if (__file_off != off_type(-1)) |
842 | { |
843 | __ret = __file_off + __computed_off; |
844 | __ret.state(__state); |
845 | } |
846 | } |
847 | } |
848 | return __ret; |
849 | } |
850 | |
851 | // _GLIBCXX_RESOLVE_LIB_DEFECTS |
852 | // 171. Strange seekpos() semantics due to joint position |
853 | // According to the resolution of DR 171, seekpos should ignore the last |
854 | // argument (of type openmode). |
855 | template<typename _CharT, typename _Traits> |
856 | typename basic_filebuf<_CharT, _Traits>::pos_type |
857 | basic_filebuf<_CharT, _Traits>:: |
858 | seekpos(pos_type __pos, ios_base::openmode) |
859 | { |
860 | pos_type __ret = pos_type(off_type(-1)); |
861 | if (this->is_open()) |
862 | { |
863 | // Ditch any pback buffers to avoid confusion. |
864 | _M_destroy_pback(); |
865 | __ret = _M_seek(off_type(__pos), ios_base::beg, __pos.state()); |
866 | } |
867 | return __ret; |
868 | } |
869 | |
870 | template<typename _CharT, typename _Traits> |
871 | typename basic_filebuf<_CharT, _Traits>::pos_type |
872 | basic_filebuf<_CharT, _Traits>:: |
873 | _M_seek(off_type __off, ios_base::seekdir __way, __state_type __state) |
874 | { |
875 | pos_type __ret = pos_type(off_type(-1)); |
876 | if (_M_terminate_output()) |
877 | { |
878 | off_type __file_off = _M_file.seekoff(__off, __way); |
879 | if (__file_off != off_type(-1)) |
880 | { |
881 | _M_reading = false; |
882 | _M_writing = false; |
883 | _M_ext_next = _M_ext_end = _M_ext_buf; |
884 | _M_set_buffer(-1); |
885 | _M_state_cur = __state; |
886 | __ret = __file_off; |
887 | __ret.state(_M_state_cur); |
888 | } |
889 | } |
890 | return __ret; |
891 | } |
892 | |
893 | // Returns the distance from the end of the ext buffer to the point |
894 | // corresponding to gptr(). This is a negative value. Updates __state |
895 | // from eback() correspondence to gptr(). |
896 | template<typename _CharT, typename _Traits> |
897 | int basic_filebuf<_CharT, _Traits>:: |
898 | _M_get_ext_pos(__state_type& __state) |
899 | { |
900 | if (_M_codecvt->always_noconv()) |
901 | return this->gptr() - this->egptr(); |
902 | else |
903 | { |
904 | // Calculate offset from _M_ext_buf that corresponds to |
905 | // gptr(). Precondition: __state == _M_state_last, which |
906 | // corresponds to eback(). |
907 | const int __gptr_off = |
908 | _M_codecvt->length(__state, _M_ext_buf, _M_ext_next, |
909 | this->gptr() - this->eback()); |
910 | return _M_ext_buf + __gptr_off - _M_ext_end; |
911 | } |
912 | } |
913 | |
914 | template<typename _CharT, typename _Traits> |
915 | bool |
916 | basic_filebuf<_CharT, _Traits>:: |
917 | _M_terminate_output() |
918 | { |
919 | // Part one: update the output sequence. |
920 | bool __testvalid = true; |
921 | if (this->pbase() < this->pptr()) |
922 | { |
923 | const int_type __tmp = this->overflow(); |
924 | if (traits_type::eq_int_type(__tmp, traits_type::eof())) |
925 | __testvalid = false; |
926 | } |
927 | |
928 | // Part two: output unshift sequence. |
929 | if (_M_writing && !__check_facet(_M_codecvt).always_noconv() |
930 | && __testvalid) |
931 | { |
932 | // Note: this value is arbitrary, since there is no way to |
933 | // get the length of the unshift sequence from codecvt, |
934 | // without calling unshift. |
935 | const size_t __blen = 128; |
936 | char __buf[__blen]; |
937 | codecvt_base::result __r; |
938 | streamsize __ilen = 0; |
939 | |
940 | do |
941 | { |
942 | char* __next; |
943 | __r = _M_codecvt->unshift(_M_state_cur, __buf, |
944 | __buf + __blen, __next); |
945 | if (__r == codecvt_base::error) |
946 | __testvalid = false; |
947 | else if (__r == codecvt_base::ok || |
948 | __r == codecvt_base::partial) |
949 | { |
950 | __ilen = __next - __buf; |
951 | if (__ilen > 0) |
952 | { |
953 | const streamsize __elen = _M_file.xsputn(__buf, __ilen); |
954 | if (__elen != __ilen) |
955 | __testvalid = false; |
956 | } |
957 | } |
958 | } |
959 | while (__r == codecvt_base::partial && __ilen > 0 && __testvalid); |
960 | |
961 | if (__testvalid) |
962 | { |
963 | // This second call to overflow() is required by the standard, |
964 | // but it's not clear why it's needed, since the output buffer |
965 | // should be empty by this point (it should have been emptied |
966 | // in the first call to overflow()). |
967 | const int_type __tmp = this->overflow(); |
968 | if (traits_type::eq_int_type(__tmp, traits_type::eof())) |
969 | __testvalid = false; |
970 | } |
971 | } |
972 | return __testvalid; |
973 | } |
974 | |
975 | template<typename _CharT, typename _Traits> |
976 | int |
977 | basic_filebuf<_CharT, _Traits>:: |
978 | sync() |
979 | { |
980 | // Make sure that the internal buffer resyncs its idea of |
981 | // the file position with the external file. |
982 | int __ret = 0; |
983 | if (this->pbase() < this->pptr()) |
984 | { |
985 | const int_type __tmp = this->overflow(); |
986 | if (traits_type::eq_int_type(__tmp, traits_type::eof())) |
987 | __ret = -1; |
988 | } |
989 | return __ret; |
990 | } |
991 | |
992 | template<typename _CharT, typename _Traits> |
993 | void |
994 | basic_filebuf<_CharT, _Traits>:: |
995 | imbue(const locale& __loc) |
996 | { |
997 | bool __testvalid = true; |
998 | |
999 | const __codecvt_type* _M_codecvt_tmp = 0; |
1000 | if (__builtin_expect(has_facet<__codecvt_type>(__loc), true)) |
1001 | _M_codecvt_tmp = &use_facet<__codecvt_type>(__loc); |
1002 | |
1003 | if (this->is_open()) |
1004 | { |
1005 | // encoding() == -1 is ok only at the beginning. |
1006 | if ((_M_reading || _M_writing) |
1007 | && __check_facet(_M_codecvt).encoding() == -1) |
1008 | __testvalid = false; |
1009 | else |
1010 | { |
1011 | if (_M_reading) |
1012 | { |
1013 | if (__check_facet(_M_codecvt).always_noconv()) |
1014 | { |
1015 | if (_M_codecvt_tmp |
1016 | && !__check_facet(_M_codecvt_tmp).always_noconv()) |
1017 | __testvalid = this->seekoff(0, ios_base::cur, _M_mode) |
1018 | != pos_type(off_type(-1)); |
1019 | } |
1020 | else |
1021 | { |
1022 | // External position corresponding to gptr(). |
1023 | _M_ext_next = _M_ext_buf |
1024 | + _M_codecvt->length(_M_state_last, _M_ext_buf, |
1025 | _M_ext_next, |
1026 | this->gptr() - this->eback()); |
1027 | const streamsize __remainder = _M_ext_end - _M_ext_next; |
1028 | if (__remainder) |
1029 | __builtin_memmove(_M_ext_buf, _M_ext_next, __remainder); |
1030 | |
1031 | _M_ext_next = _M_ext_buf; |
1032 | _M_ext_end = _M_ext_buf + __remainder; |
1033 | _M_set_buffer(-1); |
1034 | _M_state_last = _M_state_cur = _M_state_beg; |
1035 | } |
1036 | } |
1037 | else if (_M_writing && (__testvalid = _M_terminate_output())) |
1038 | _M_set_buffer(-1); |
1039 | } |
1040 | } |
1041 | |
1042 | if (__testvalid) |
1043 | _M_codecvt = _M_codecvt_tmp; |
1044 | else |
1045 | _M_codecvt = 0; |
1046 | } |
1047 | |
1048 | // Inhibit implicit instantiations for required instantiations, |
1049 | // which are defined via explicit instantiations elsewhere. |
1050 | #if _GLIBCXX_EXTERN_TEMPLATE |
1051 | extern template class basic_filebuf<char>; |
1052 | extern template class basic_ifstream<char>; |
1053 | extern template class basic_ofstream<char>; |
1054 | extern template class basic_fstream<char>; |
1055 | |
1056 | #ifdef _GLIBCXX_USE_WCHAR_T |
1057 | extern template class basic_filebuf<wchar_t>; |
1058 | extern template class basic_ifstream<wchar_t>; |
1059 | extern template class basic_ofstream<wchar_t>; |
1060 | extern template class basic_fstream<wchar_t>; |
1061 | #endif |
1062 | #endif |
1063 | |
1064 | _GLIBCXX_END_NAMESPACE_VERSION |
1065 | } // namespace std |
1066 | |
1067 | #endif |
1068 | |