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