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