1 | // Iostreams wrapper for stdio FILE* -*- C++ -*- |
2 | |
3 | // Copyright (C) 2003-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 ext/stdio_sync_filebuf.h |
26 | * This file is a GNU extension to the Standard C++ Library. |
27 | */ |
28 | |
29 | #ifndef _STDIO_SYNC_FILEBUF_H |
30 | #define _STDIO_SYNC_FILEBUF_H 1 |
31 | |
32 | #pragma GCC system_header |
33 | |
34 | #include <streambuf> |
35 | #include <unistd.h> |
36 | #include <cstdio> |
37 | #include <bits/c++io.h> // For __c_file |
38 | #include <bits/move.h> // For __exchange |
39 | |
40 | #ifdef _GLIBCXX_USE_WCHAR_T |
41 | #include <cwchar> |
42 | #endif |
43 | |
44 | namespace __gnu_cxx _GLIBCXX_VISIBILITY(default) |
45 | { |
46 | _GLIBCXX_BEGIN_NAMESPACE_VERSION |
47 | |
48 | /** |
49 | * @brief Provides a layer of compatibility for C. |
50 | * @ingroup io |
51 | * |
52 | * This GNU extension provides extensions for working with standard |
53 | * C FILE*'s. It must be instantiated by the user with the type of |
54 | * character used in the file stream, e.g., stdio_filebuf<char>. |
55 | */ |
56 | template<typename _CharT, typename _Traits = std::char_traits<_CharT> > |
57 | class stdio_sync_filebuf : public std::basic_streambuf<_CharT, _Traits> |
58 | { |
59 | public: |
60 | // Types: |
61 | typedef _CharT char_type; |
62 | typedef _Traits traits_type; |
63 | typedef typename traits_type::int_type int_type; |
64 | typedef typename traits_type::pos_type pos_type; |
65 | typedef typename traits_type::off_type off_type; |
66 | |
67 | private: |
68 | typedef std::basic_streambuf<_CharT, _Traits> __streambuf_type; |
69 | |
70 | // Underlying stdio FILE |
71 | std::__c_file* _M_file; |
72 | |
73 | // Last character gotten. This is used when pbackfail is |
74 | // called from basic_streambuf::sungetc() |
75 | int_type _M_unget_buf; |
76 | |
77 | public: |
78 | explicit |
79 | stdio_sync_filebuf(std::__c_file* __f) |
80 | : _M_file(__f), _M_unget_buf(traits_type::eof()) |
81 | { } |
82 | |
83 | #if __cplusplus >= 201103L |
84 | stdio_sync_filebuf(stdio_sync_filebuf&& __fb) noexcept |
85 | : __streambuf_type(std::move(__fb)), |
86 | _M_file(__fb._M_file), _M_unget_buf(__fb._M_unget_buf) |
87 | { |
88 | __fb._M_file = nullptr; |
89 | __fb._M_unget_buf = traits_type::eof(); |
90 | } |
91 | |
92 | stdio_sync_filebuf& |
93 | operator=(stdio_sync_filebuf&& __fb) noexcept |
94 | { |
95 | __streambuf_type::operator=(__fb); |
96 | _M_file = std::__exchange(__fb._M_file, nullptr); |
97 | _M_unget_buf = std::__exchange(__fb._M_unget_buf, traits_type::eof()); |
98 | return *this; |
99 | } |
100 | |
101 | void |
102 | swap(stdio_sync_filebuf& __fb) |
103 | { |
104 | __streambuf_type::swap(__fb); |
105 | std::swap(_M_file, __fb._M_file); |
106 | std::swap(_M_unget_buf, __fb._M_unget_buf); |
107 | } |
108 | #endif |
109 | |
110 | /** |
111 | * @return The underlying FILE*. |
112 | * |
113 | * This function can be used to access the underlying C file pointer. |
114 | * Note that there is no way for the library to track what you do |
115 | * with the file, so be careful. |
116 | */ |
117 | std::__c_file* |
118 | file() { return this->_M_file; } |
119 | |
120 | protected: |
121 | int_type |
122 | syncgetc(); |
123 | |
124 | int_type |
125 | syncungetc(int_type __c); |
126 | |
127 | int_type |
128 | syncputc(int_type __c); |
129 | |
130 | virtual int_type |
131 | underflow() |
132 | { |
133 | int_type __c = this->syncgetc(); |
134 | return this->syncungetc(__c); |
135 | } |
136 | |
137 | virtual int_type |
138 | uflow() |
139 | { |
140 | // Store the gotten character in case we need to unget it. |
141 | _M_unget_buf = this->syncgetc(); |
142 | return _M_unget_buf; |
143 | } |
144 | |
145 | virtual int_type |
146 | pbackfail(int_type __c = traits_type::eof()) |
147 | { |
148 | int_type __ret; |
149 | const int_type __eof = traits_type::eof(); |
150 | |
151 | // Check if the unget or putback was requested |
152 | if (traits_type::eq_int_type(__c, __eof)) // unget |
153 | { |
154 | if (!traits_type::eq_int_type(_M_unget_buf, __eof)) |
155 | __ret = this->syncungetc(_M_unget_buf); |
156 | else // buffer invalid, fail. |
157 | __ret = __eof; |
158 | } |
159 | else // putback |
160 | __ret = this->syncungetc(__c); |
161 | |
162 | // The buffered character is no longer valid, discard it. |
163 | _M_unget_buf = __eof; |
164 | return __ret; |
165 | } |
166 | |
167 | virtual std::streamsize |
168 | xsgetn(char_type* __s, std::streamsize __n); |
169 | |
170 | virtual int_type |
171 | overflow(int_type __c = traits_type::eof()) |
172 | { |
173 | int_type __ret; |
174 | if (traits_type::eq_int_type(__c, traits_type::eof())) |
175 | { |
176 | if (std::fflush(_M_file)) |
177 | __ret = traits_type::eof(); |
178 | else |
179 | __ret = traits_type::not_eof(__c); |
180 | } |
181 | else |
182 | __ret = this->syncputc(__c); |
183 | return __ret; |
184 | } |
185 | |
186 | virtual std::streamsize |
187 | xsputn(const char_type* __s, std::streamsize __n); |
188 | |
189 | virtual int |
190 | sync() |
191 | { return std::fflush(_M_file); } |
192 | |
193 | virtual std::streampos |
194 | seekoff(std::streamoff __off, std::ios_base::seekdir __dir, |
195 | std::ios_base::openmode = std::ios_base::in | std::ios_base::out) |
196 | { |
197 | std::streampos __ret(std::streamoff(-1)); |
198 | int __whence; |
199 | if (__dir == std::ios_base::beg) |
200 | __whence = SEEK_SET; |
201 | else if (__dir == std::ios_base::cur) |
202 | __whence = SEEK_CUR; |
203 | else |
204 | __whence = SEEK_END; |
205 | #ifdef _GLIBCXX_USE_LFS |
206 | if (!fseeko64(_M_file, __off, __whence)) |
207 | __ret = std::streampos(ftello64(_M_file)); |
208 | #else |
209 | if (!fseek(_M_file, __off, __whence)) |
210 | __ret = std::streampos(std::ftell(_M_file)); |
211 | #endif |
212 | return __ret; |
213 | } |
214 | |
215 | virtual std::streampos |
216 | seekpos(std::streampos __pos, |
217 | std::ios_base::openmode __mode = |
218 | std::ios_base::in | std::ios_base::out) |
219 | { return seekoff(std::streamoff(__pos), std::ios_base::beg, __mode); } |
220 | }; |
221 | |
222 | template<> |
223 | inline stdio_sync_filebuf<char>::int_type |
224 | stdio_sync_filebuf<char>::syncgetc() |
225 | { return std::getc(_M_file); } |
226 | |
227 | template<> |
228 | inline stdio_sync_filebuf<char>::int_type |
229 | stdio_sync_filebuf<char>::syncungetc(int_type __c) |
230 | { return std::ungetc(__c, _M_file); } |
231 | |
232 | template<> |
233 | inline stdio_sync_filebuf<char>::int_type |
234 | stdio_sync_filebuf<char>::syncputc(int_type __c) |
235 | { return std::putc(__c, _M_file); } |
236 | |
237 | template<> |
238 | inline std::streamsize |
239 | stdio_sync_filebuf<char>::xsgetn(char* __s, std::streamsize __n) |
240 | { |
241 | std::streamsize __ret = std::fread(__s, 1, __n, _M_file); |
242 | if (__ret > 0) |
243 | _M_unget_buf = traits_type::to_int_type(__s[__ret - 1]); |
244 | else |
245 | _M_unget_buf = traits_type::eof(); |
246 | return __ret; |
247 | } |
248 | |
249 | template<> |
250 | inline std::streamsize |
251 | stdio_sync_filebuf<char>::xsputn(const char* __s, std::streamsize __n) |
252 | { return std::fwrite(__s, 1, __n, _M_file); } |
253 | |
254 | #ifdef _GLIBCXX_USE_WCHAR_T |
255 | template<> |
256 | inline stdio_sync_filebuf<wchar_t>::int_type |
257 | stdio_sync_filebuf<wchar_t>::syncgetc() |
258 | { return std::getwc(_M_file); } |
259 | |
260 | template<> |
261 | inline stdio_sync_filebuf<wchar_t>::int_type |
262 | stdio_sync_filebuf<wchar_t>::syncungetc(int_type __c) |
263 | { return std::ungetwc(__c, _M_file); } |
264 | |
265 | template<> |
266 | inline stdio_sync_filebuf<wchar_t>::int_type |
267 | stdio_sync_filebuf<wchar_t>::syncputc(int_type __c) |
268 | { return std::putwc(__c, _M_file); } |
269 | |
270 | template<> |
271 | inline std::streamsize |
272 | stdio_sync_filebuf<wchar_t>::xsgetn(wchar_t* __s, std::streamsize __n) |
273 | { |
274 | std::streamsize __ret = 0; |
275 | const int_type __eof = traits_type::eof(); |
276 | while (__n--) |
277 | { |
278 | int_type __c = this->syncgetc(); |
279 | if (traits_type::eq_int_type(__c, __eof)) |
280 | break; |
281 | __s[__ret] = traits_type::to_char_type(__c); |
282 | ++__ret; |
283 | } |
284 | |
285 | if (__ret > 0) |
286 | _M_unget_buf = traits_type::to_int_type(__s[__ret - 1]); |
287 | else |
288 | _M_unget_buf = traits_type::eof(); |
289 | return __ret; |
290 | } |
291 | |
292 | template<> |
293 | inline std::streamsize |
294 | stdio_sync_filebuf<wchar_t>::xsputn(const wchar_t* __s, |
295 | std::streamsize __n) |
296 | { |
297 | std::streamsize __ret = 0; |
298 | const int_type __eof = traits_type::eof(); |
299 | while (__n--) |
300 | { |
301 | if (traits_type::eq_int_type(this->syncputc(*__s++), __eof)) |
302 | break; |
303 | ++__ret; |
304 | } |
305 | return __ret; |
306 | } |
307 | #endif |
308 | |
309 | #if _GLIBCXX_EXTERN_TEMPLATE |
310 | extern template class stdio_sync_filebuf<char>; |
311 | #ifdef _GLIBCXX_USE_WCHAR_T |
312 | extern template class stdio_sync_filebuf<wchar_t>; |
313 | #endif |
314 | #endif |
315 | |
316 | _GLIBCXX_END_NAMESPACE_VERSION |
317 | } // namespace |
318 | |
319 | #endif |
320 | |