Boost.Nowide
filebuf.hpp
1 //
2 // Copyright (c) 2012 Artyom Beilis (Tonkikh)
3 // Copyright (c) 2019-2020 Alexander Grund
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See
6 // accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8 //
9 #ifndef BOOST_NOWIDE_FILEBUF_HPP_INCLUDED
10 #define BOOST_NOWIDE_FILEBUF_HPP_INCLUDED
11 
12 #include <boost/nowide/config.hpp>
13 #if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
14 #include <boost/nowide/cstdio.hpp>
15 #include <boost/nowide/stackstring.hpp>
16 #include <cassert>
17 #include <cstdio>
18 #include <iosfwd>
19 #include <limits>
20 #include <locale>
21 #include <stdexcept>
22 #include <streambuf>
23 #else
24 #include <fstream>
25 #endif
26 
27 namespace boost {
28 namespace nowide {
29 #if !BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT && !defined(BOOST_NOWIDE_DOXYGEN)
30  using std::basic_filebuf;
31  using std::filebuf;
32 #else // Windows
33  template<typename CharType, typename Traits = std::char_traits<CharType> >
41 
48  template<>
49  class basic_filebuf<char> : public std::basic_streambuf<char>
50  {
51  typedef std::char_traits<char> Traits;
52 
53  public:
54 #ifdef BOOST_MSVC
55 #pragma warning(push)
56 #pragma warning(disable : 4351) // new behavior : elements of array will be default initialized
57 #endif
58  basic_filebuf() :
62  buffer_size_(BUFSIZ), buffer_(0), file_(0), owns_buffer_(false), last_char_(),
63  mode_(std::ios_base::openmode(0))
64  {
65  setg(0, 0, 0);
66  setp(0, 0);
67  }
68 #ifdef BOOST_MSVC
69 #pragma warning(pop)
70 #endif
71 #if !BOOST_NOWIDE_CXX11
72  private:
73  // Non-copyable
75  basic_filebuf& operator=(const basic_filebuf&);
76 
77  public:
78 #else
79  basic_filebuf(const basic_filebuf&) = delete;
80  basic_filebuf& operator=(const basic_filebuf&) = delete;
81  basic_filebuf(basic_filebuf&& other) noexcept : basic_filebuf()
82  {
83  swap(other);
84  }
85  basic_filebuf& operator=(basic_filebuf&& other) noexcept
86  {
87  swap(other);
88  return *this;
89  }
90  void swap(basic_filebuf& rhs)
91  {
92  std::basic_streambuf<char>::swap(rhs);
93  using std::swap;
94  swap(buffer_size_, rhs.buffer_size_);
95  swap(buffer_, rhs.buffer_);
96  swap(file_, rhs.file_);
97  swap(owns_buffer_, rhs.owns_buffer_);
98  swap(last_char_[0], rhs.last_char_[0]);
99  swap(mode_, rhs.mode_);
100  // Fixup last_char references
101  if(epptr() == rhs.last_char_)
102  setp(last_char_, last_char_);
103  if(egptr() == rhs.last_char_)
104  rhs.setg(last_char_, gptr() == rhs.last_char_ ? last_char_ : last_char_ + 1, last_char_ + 1);
105  if(rhs.epptr() == last_char_)
106  setp(rhs.last_char_, rhs.last_char_);
107  if(rhs.egptr() == rhs.last_char_)
108  {
109  rhs.setg(rhs.last_char_,
110  rhs.gptr() == last_char_ ? rhs.last_char_ : rhs.last_char_ + 1,
111  rhs.last_char_ + 1);
112  }
113  }
114 #endif
115  virtual ~basic_filebuf()
116  {
117  close();
118  }
119 
123  basic_filebuf* open(const std::string& s, std::ios_base::openmode mode)
124  {
125  return open(s.c_str(), mode);
126  }
130  basic_filebuf* open(const char* s, std::ios_base::openmode mode)
131  {
132  const wstackstring name(s);
133  return open(name.get(), mode);
134  }
136  basic_filebuf* open(const wchar_t* s, std::ios_base::openmode mode)
137  {
138  if(is_open())
139  return NULL;
140  validate_cvt(this->getloc());
141  const bool ate = (mode & std::ios_base::ate) != 0;
142  if(ate)
143  mode &= ~std::ios_base::ate;
144  const wchar_t* smode = get_mode(mode);
145  if(!smode)
146  return 0;
147  file_ = detail::wfopen(s, smode);
148  if(!file_)
149  return 0;
150  if(ate && std::fseek(file_, 0, SEEK_END) != 0)
151  {
152  close();
153  return 0;
154  }
155  mode_ = mode;
156  return this;
157  }
162  {
163  if(!is_open())
164  return NULL;
165  bool res = sync() == 0;
166  if(std::fclose(file_) != 0)
167  res = false;
168  file_ = NULL;
169  mode_ = std::ios_base::openmode(0);
170  if(owns_buffer_)
171  {
172  delete[] buffer_;
173  buffer_ = NULL;
174  owns_buffer_ = false;
175  }
176  return res ? this : NULL;
177  }
181  bool is_open() const
182  {
183  return file_ != NULL;
184  }
185 
186  private:
187  void make_buffer()
188  {
189  if(buffer_)
190  return;
191  if(buffer_size_ > 0)
192  {
193  buffer_ = new char[buffer_size_];
194  owns_buffer_ = true;
195  }
196  }
197  void validate_cvt(const std::locale& loc)
198  {
199  if(!std::use_facet<std::codecvt<char, char, std::mbstate_t> >(loc).always_noconv())
200  throw std::runtime_error("Converting codecvts are not supported");
201  }
202 
203  protected:
204  virtual std::streambuf* setbuf(char* s, std::streamsize n)
205  {
206  assert(n >= 0);
207  // Maximum compatibility: Discard all local buffers and use user-provided values
208  // Users should call sync() before or better use it before any IO is done or any file is opened
209  setg(NULL, NULL, NULL);
210  setp(NULL, NULL);
211  if(owns_buffer_)
212  delete[] buffer_;
213  buffer_ = s;
214  buffer_size_ = (n >= 0) ? static_cast<size_t>(n) : 0;
215  return this;
216  }
217 
218  virtual int overflow(int c = EOF)
219  {
220  if(!(mode_ & std::ios_base::out))
221  return EOF;
222 
223  if(!stop_reading())
224  return EOF;
225 
226  size_t n = pptr() - pbase();
227  if(n > 0)
228  {
229  if(std::fwrite(pbase(), 1, n, file_) != n)
230  return -1;
231  setp(buffer_, buffer_ + buffer_size_);
232  if(c != EOF)
233  {
234  *buffer_ = Traits::to_char_type(c);
235  pbump(1);
236  }
237  } else if(c != EOF)
238  {
239  if(buffer_size_ > 0)
240  {
241  make_buffer();
242  setp(buffer_, buffer_ + buffer_size_);
243  *buffer_ = Traits::to_char_type(c);
244  pbump(1);
245  } else if(std::fputc(c, file_) == EOF)
246  {
247  return EOF;
248  } else if(!pptr())
249  {
250  // Set to dummy value so we know we have written something
251  setp(last_char_, last_char_);
252  }
253  }
254  return Traits::not_eof(c);
255  }
256 
257  virtual int sync()
258  {
259  if(!file_)
260  return 0;
261  bool result;
262  if(pptr())
263  {
264  result = overflow() != EOF;
265  // Only flush if anything was written, otherwise behavior of fflush is undefined
266  if(std::fflush(file_) != 0)
267  return result = false;
268  } else
269  result = stop_reading();
270  return result ? 0 : -1;
271  }
272 
273  virtual int underflow()
274  {
275  if(!(mode_ & std::ios_base::in))
276  return EOF;
277  if(!stop_writing())
278  return EOF;
279  if(buffer_size_ == 0)
280  {
281  const int c = std::fgetc(file_);
282  if(c == EOF)
283  return EOF;
284  last_char_[0] = Traits::to_char_type(c);
285  setg(last_char_, last_char_, last_char_ + 1);
286  } else
287  {
288  make_buffer();
289  const size_t n = std::fread(buffer_, 1, buffer_size_, file_);
290  setg(buffer_, buffer_, buffer_ + n);
291  if(n == 0)
292  return EOF;
293  }
294  return Traits::to_int_type(*gptr());
295  }
296 
297  virtual int pbackfail(int c = EOF)
298  {
299  if(!(mode_ & std::ios_base::in))
300  return EOF;
301  if(!stop_writing())
302  return EOF;
303  if(gptr() > eback())
304  gbump(-1);
305  else if(seekoff(-1, std::ios_base::cur) != std::streampos(std::streamoff(-1)))
306  {
307  if(underflow() == EOF)
308  return EOF;
309  } else
310  return EOF;
311 
312  // Case 1: Caller just wanted space for 1 char
313  if(c == EOF)
314  return Traits::not_eof(c);
315  // Case 2: Caller wants to put back different char
316  // gptr now points to the (potentially newly read) previous char
317  if(*gptr() != c)
318  *gptr() = Traits::to_char_type(c);
319  return Traits::not_eof(c);
320  }
321 
322  virtual std::streampos seekoff(std::streamoff off,
323  std::ios_base::seekdir seekdir,
324  std::ios_base::openmode = std::ios_base::in | std::ios_base::out)
325  {
326  if(!file_)
327  return EOF;
328  // Switching between input<->output requires a seek
329  // So do NOT optimize for seekoff(0, cur) as No-OP
330 
331  // On some implementations a seek also flushes, so do a full sync
332  if(sync() != 0)
333  return EOF;
334  int whence;
335  switch(seekdir)
336  {
337  case std::ios_base::beg: whence = SEEK_SET; break;
338  case std::ios_base::cur: whence = SEEK_CUR; break;
339  case std::ios_base::end: whence = SEEK_END; break;
340  default: assert(false); return EOF;
341  }
342  assert(off <= std::numeric_limits<long>::max());
343  if(std::fseek(file_, static_cast<long>(off), whence) != 0)
344  return EOF;
345  return std::ftell(file_);
346  }
347  virtual std::streampos seekpos(std::streampos pos,
348  std::ios_base::openmode m = std::ios_base::in | std::ios_base::out)
349  {
350  // Standard mandates "as-if fsetpos", but assume the effect is the same as fseek
351  return seekoff(pos, std::ios_base::beg, m);
352  }
353  virtual void imbue(const std::locale& loc)
354  {
355  validate_cvt(loc);
356  }
357 
358  private:
361  bool stop_reading()
362  {
363  if(gptr())
364  {
365  const std::streamsize off = gptr() - egptr();
366  setg(0, 0, 0);
367  assert(off <= std::numeric_limits<long>::max());
368  if(off && std::fseek(file_, static_cast<long>(off), SEEK_CUR) != 0)
369  return false;
370  }
371  return true;
372  }
373 
376  bool stop_writing()
377  {
378  if(pptr())
379  {
380  const char* const base = pbase();
381  const size_t n = pptr() - base;
382  setp(0, 0);
383  if(n && std::fwrite(base, 1, n, file_) != n)
384  return false;
385  }
386  return true;
387  }
388 
389  void reset(FILE* f = 0)
390  {
391  sync();
392  if(file_)
393  {
394  fclose(file_);
395  file_ = 0;
396  }
397  file_ = f;
398  }
399 
400  static const wchar_t* get_mode(std::ios_base::openmode mode)
401  {
402  //
403  // done according to n2914 table 106 27.9.1.4
404  //
405 
406  // note can't use switch case as overload operator can't be used
407  // in constant expression
408  if(mode == (std::ios_base::out))
409  return L"w";
410  if(mode == (std::ios_base::out | std::ios_base::app))
411  return L"a";
412  if(mode == (std::ios_base::app))
413  return L"a";
414  if(mode == (std::ios_base::out | std::ios_base::trunc))
415  return L"w";
416  if(mode == (std::ios_base::in))
417  return L"r";
418  if(mode == (std::ios_base::in | std::ios_base::out))
419  return L"r+";
420  if(mode == (std::ios_base::in | std::ios_base::out | std::ios_base::trunc))
421  return L"w+";
422  if(mode == (std::ios_base::in | std::ios_base::out | std::ios_base::app))
423  return L"a+";
424  if(mode == (std::ios_base::in | std::ios_base::app))
425  return L"a+";
426  if(mode == (std::ios_base::binary | std::ios_base::out))
427  return L"wb";
428  if(mode == (std::ios_base::binary | std::ios_base::out | std::ios_base::app))
429  return L"ab";
430  if(mode == (std::ios_base::binary | std::ios_base::app))
431  return L"ab";
432  if(mode == (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc))
433  return L"wb";
434  if(mode == (std::ios_base::binary | std::ios_base::in))
435  return L"rb";
436  if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out))
437  return L"r+b";
438  if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::trunc))
439  return L"w+b";
440  if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::app))
441  return L"a+b";
442  if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::app))
443  return L"a+b";
444  return 0;
445  }
446 
447  size_t buffer_size_;
448  char* buffer_;
449  FILE* file_;
450  bool owns_buffer_;
451  char last_char_[1];
452  std::ios::openmode mode_;
453  };
454 
459 
460 #endif // windows
461 
462 } // namespace nowide
463 } // namespace boost
464 
465 #endif
basic_filebuf * close()
Definition: filebuf.hpp:161
bool is_open() const
Definition: filebuf.hpp:181
basic_filebuf< char > filebuf
Convenience typedef.
Definition: filebuf.hpp:458
This forward declaration defines the basic_filebuf type.
Definition: filebuf.hpp:40
basic_filebuf * open(const wchar_t *s, std::ios_base::openmode mode)
Opens the file with the given name, see std::filebuf::open.
Definition: filebuf.hpp:136
basic_filebuf * open(const char *s, std::ios_base::openmode mode)
Definition: filebuf.hpp:130
This is the implementation of std::filebuf.
Definition: filebuf.hpp:49
A class that allows to create a temporary wide or narrow UTF strings from wide or narrow UTF source.
Definition: stackstring.hpp:31
basic_filebuf * open(const std::string &s, std::ios_base::openmode mode)
Definition: filebuf.hpp:123
output_char * get()
Return the converted, NULL-terminated string or NULL if no string was converted.
Definition: stackstring.hpp:126