File indexing completed on 2025-01-18 09:42:45
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_NOWIDE_FILEBUF_HPP_INCLUDED
0009 #define BOOST_NOWIDE_FILEBUF_HPP_INCLUDED
0010
0011 #include <boost/nowide/config.hpp>
0012 #if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
0013 #include <boost/nowide/cstdio.hpp>
0014 #include <boost/nowide/detail/is_path.hpp>
0015 #include <boost/nowide/stackstring.hpp>
0016 #include <cassert>
0017 #include <cstdio>
0018 #include <ios>
0019 #include <limits>
0020 #include <locale>
0021 #include <stdexcept>
0022 #include <streambuf>
0023 #else
0024 #include <fstream>
0025 #endif
0026
0027 namespace boost {
0028 namespace nowide {
0029 namespace detail {
0030
0031 BOOST_NOWIDE_DECL std::streampos ftell(FILE* file);
0032
0033 BOOST_NOWIDE_DECL int fseek(FILE* file, std::streamoff offset, int origin);
0034 }
0035
0036 #if !BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT && !defined(BOOST_NOWIDE_DOXYGEN)
0037 using std::basic_filebuf;
0038 using std::filebuf;
0039 #else
0040
0041
0042
0043
0044
0045
0046
0047 template<typename CharType, typename Traits = std::char_traits<CharType>>
0048 class basic_filebuf;
0049
0050
0051
0052
0053
0054
0055
0056
0057 template<>
0058 class basic_filebuf<char> : public std::basic_streambuf<char>
0059 {
0060 using Traits = std::char_traits<char>;
0061
0062 public:
0063 #ifdef BOOST_MSVC
0064 #pragma warning(push)
0065 #pragma warning(disable : 4351)
0066 #endif
0067
0068
0069
0070 basic_filebuf() :
0071 file_(nullptr), buffer_(nullptr), buffer_size_(BUFSIZ), owns_buffer_(false), unbuffered_read_(false),
0072 last_char_(), mode_(std::ios_base::openmode(0))
0073 {
0074 setg(nullptr, nullptr, nullptr);
0075 setp(nullptr, nullptr);
0076 }
0077 #ifdef BOOST_MSVC
0078 #pragma warning(pop)
0079 #endif
0080 basic_filebuf(const basic_filebuf&) = delete;
0081 basic_filebuf& operator=(const basic_filebuf&) = delete;
0082 basic_filebuf(basic_filebuf&& other) noexcept : basic_filebuf()
0083 {
0084 swap(other);
0085 }
0086 basic_filebuf& operator=(basic_filebuf&& other) noexcept
0087 {
0088 close();
0089 swap(other);
0090 return *this;
0091 }
0092 void swap(basic_filebuf& rhs)
0093 {
0094 std::basic_streambuf<char>::swap(rhs);
0095 using std::swap;
0096 swap(file_, rhs.file_);
0097 swap(buffer_, rhs.buffer_);
0098 swap(buffer_size_, rhs.buffer_size_);
0099 swap(owns_buffer_, rhs.owns_buffer_);
0100 swap(unbuffered_read_, rhs.unbuffered_read_);
0101 swap(last_char_[0], rhs.last_char_[0]);
0102 swap(mode_, rhs.mode_);
0103
0104
0105 if(pbase() == rhs.last_char_)
0106 setp(last_char_, (pptr() == epptr()) ? last_char_ : last_char_ + 1);
0107 if(eback() == rhs.last_char_)
0108 setg(last_char_, (gptr() == rhs.last_char_) ? last_char_ : last_char_ + 1, last_char_ + 1);
0109
0110 if(rhs.pbase() == last_char_)
0111 rhs.setp(rhs.last_char_, (rhs.pptr() == rhs.epptr()) ? rhs.last_char_ : rhs.last_char_ + 1);
0112 if(rhs.eback() == last_char_)
0113 {
0114 rhs.setg(rhs.last_char_,
0115 (rhs.gptr() == last_char_) ? rhs.last_char_ : rhs.last_char_ + 1,
0116 rhs.last_char_ + 1);
0117 }
0118 }
0119
0120 virtual ~basic_filebuf()
0121 {
0122 close();
0123 }
0124
0125
0126
0127
0128 basic_filebuf* open(const std::string& s, std::ios_base::openmode mode)
0129 {
0130 return open(s.c_str(), mode);
0131 }
0132
0133
0134
0135 basic_filebuf* open(const char* s, std::ios_base::openmode mode)
0136 {
0137 const wstackstring name(s);
0138 return open(name.get(), mode);
0139 }
0140
0141 basic_filebuf* open(const wchar_t* s, std::ios_base::openmode mode)
0142 {
0143 if(is_open())
0144 return nullptr;
0145 validate_cvt(this->getloc());
0146 const bool ate = (mode & std::ios_base::ate) != 0;
0147 if(ate)
0148 mode &= ~std::ios_base::ate;
0149 const wchar_t* smode = get_mode(mode);
0150 if(!smode)
0151 return nullptr;
0152 file_ = detail::wfopen(s, smode);
0153 if(!file_)
0154 return nullptr;
0155 if(ate && detail::fseek(file_, 0, SEEK_END) != 0)
0156 {
0157 close();
0158 return nullptr;
0159 }
0160 mode_ = mode;
0161 set_unbuffered_read();
0162 return this;
0163 }
0164 template<typename Path>
0165 detail::enable_if_path_t<Path, basic_filebuf*> open(const Path& file_name, std::ios_base::openmode mode)
0166 {
0167 return open(file_name.c_str(), mode);
0168 }
0169
0170
0171
0172 basic_filebuf* close()
0173 {
0174 if(!is_open())
0175 return nullptr;
0176 bool res = sync() == 0;
0177 if(std::fclose(file_) != 0)
0178 res = false;
0179 file_ = nullptr;
0180 mode_ = std::ios_base::openmode(0);
0181 if(owns_buffer_)
0182 {
0183 delete[] buffer_;
0184 buffer_ = nullptr;
0185 owns_buffer_ = false;
0186 }
0187 setg(nullptr, nullptr, nullptr);
0188 setp(nullptr, nullptr);
0189 return res ? this : nullptr;
0190 }
0191
0192
0193
0194 bool is_open() const
0195 {
0196 return file_ != nullptr;
0197 }
0198
0199 protected:
0200 std::streambuf* setbuf(char* s, std::streamsize n) override
0201 {
0202 assert(n >= 0);
0203
0204
0205 setg(nullptr, nullptr, nullptr);
0206 setp(nullptr, nullptr);
0207 if(owns_buffer_)
0208 {
0209 delete[] buffer_;
0210 owns_buffer_ = false;
0211 }
0212 buffer_ = s;
0213 buffer_size_ = (n >= 0) ? static_cast<size_t>(n) : 0;
0214 set_unbuffered_read();
0215 return this;
0216 }
0217
0218 int sync() override
0219 {
0220 if(!file_)
0221 return 0;
0222 bool result;
0223 if(pptr())
0224 {
0225
0226
0227
0228 const bool has_prev_write = pptr() != buffer_;
0229 result = overflow() != EOF;
0230 if(has_prev_write && std::fflush(file_) != 0)
0231 result = false;
0232 } else
0233 result = stop_reading();
0234 return result ? 0 : -1;
0235 }
0236
0237 int overflow(int c = EOF) override
0238 {
0239 if(!(mode_ & (std::ios_base::out | std::ios_base::app)))
0240 return EOF;
0241
0242 if(!stop_reading())
0243 return EOF;
0244
0245 size_t n = pptr() - pbase();
0246 if(n > 0)
0247 {
0248 if(std::fwrite(pbase(), 1, n, file_) != n)
0249 return EOF;
0250 assert(buffer_);
0251 setp(buffer_, buffer_ + buffer_size_);
0252 if(c != EOF)
0253 {
0254 *buffer_ = Traits::to_char_type(c);
0255 pbump(1);
0256 }
0257 } else if(c != EOF)
0258 {
0259 if(buffer_size_ > 0)
0260 {
0261 make_buffer();
0262 setp(buffer_, buffer_ + buffer_size_);
0263 *buffer_ = Traits::to_char_type(c);
0264 pbump(1);
0265 } else if(std::fputc(c, file_) == EOF)
0266 {
0267 return EOF;
0268 } else if(!pptr())
0269 {
0270
0271 setp(last_char_, last_char_);
0272 }
0273 }
0274 return Traits::not_eof(c);
0275 }
0276
0277 std::streamsize xsputn(const char* s, std::streamsize n) override
0278 {
0279
0280 if(n <= static_cast<std::streamsize>(buffer_size_))
0281 return std::basic_streambuf<char>::xsputn(s, n);
0282 if(!(mode_ & (std::ios_base::out | std::ios_base::app)) || !stop_reading())
0283 return 0;
0284
0285 assert(n >= 0);
0286
0287 const char* const base = pbase();
0288 const size_t num_buffered = pptr() - base;
0289 if(num_buffered != 0)
0290 {
0291 const auto num_written = std::fwrite(base, 1, num_buffered, file_);
0292 setp(const_cast<char*>(base + num_written), epptr());
0293 if(num_written != num_buffered)
0294 return 0;
0295 }
0296
0297 const auto num_written = std::fwrite(s, 1, static_cast<size_t>(n), file_);
0298 if(num_written > 0u && base != last_char_)
0299 setp(last_char_, last_char_);
0300 return num_written;
0301 }
0302
0303 int underflow() override
0304 {
0305 if(!(mode_ & std::ios_base::in) || !stop_writing())
0306 return EOF;
0307 if(unbuffered_read_)
0308 {
0309 const int c = std::fgetc(file_);
0310 if(c == EOF)
0311 return EOF;
0312 last_char_[0] = Traits::to_char_type(c);
0313 setg(last_char_, last_char_, last_char_ + 1);
0314 } else
0315 {
0316 make_buffer();
0317 const size_t n = std::fread(buffer_, 1, buffer_size_, file_);
0318 setg(buffer_, buffer_, buffer_ + n);
0319 if(n == 0)
0320 return EOF;
0321 }
0322 return Traits::to_int_type(*gptr());
0323 }
0324
0325 std::streamsize xsgetn(char* s, std::streamsize n) override
0326 {
0327
0328 if(n <= static_cast<std::streamsize>(unbuffered_read_ ? 1u : buffer_size_))
0329 return std::basic_streambuf<char>::xsgetn(s, n);
0330 if(!(mode_ & std::ios_base::in) || !stop_writing())
0331 return 0;
0332 assert(n >= 0);
0333 std::streamsize num_copied = 0;
0334
0335 const auto num_buffered = egptr() - gptr();
0336 if(num_buffered != 0)
0337 {
0338 const auto num_read = num_buffered > n ? n : num_buffered;
0339 traits_type::copy(s, gptr(), static_cast<size_t>(num_read));
0340 s += num_read;
0341 n -= num_read;
0342 num_copied = num_read;
0343 setg(eback(), gptr() + num_read, egptr());
0344 }
0345
0346 while(n > 0)
0347 {
0348 const auto num_read = std::fread(s, 1, static_cast<size_t>(n), file_);
0349 if(num_read == 0)
0350 break;
0351 s += num_read;
0352 n -= num_read;
0353 num_copied += num_read;
0354 }
0355 return num_copied;
0356 }
0357
0358 int pbackfail(int c = EOF) override
0359 {
0360
0361
0362 if(gptr() > eback())
0363 gbump(-1);
0364 else
0365 return EOF;
0366
0367
0368 if(c != EOF && *gptr() != Traits::to_char_type(c))
0369 *gptr() = Traits::to_char_type(c);
0370 return Traits::not_eof(c);
0371 }
0372
0373 std::streampos seekoff(std::streamoff off,
0374 std::ios_base::seekdir seekdir,
0375 std::ios_base::openmode = std::ios_base::in | std::ios_base::out) override
0376 {
0377 if(!file_)
0378 return EOF;
0379
0380
0381
0382
0383 if(sync() != 0)
0384 return EOF;
0385 int whence;
0386 switch(seekdir)
0387 {
0388 case std::ios_base::beg: whence = SEEK_SET; break;
0389 case std::ios_base::cur: whence = SEEK_CUR; break;
0390 case std::ios_base::end: whence = SEEK_END; break;
0391 default: assert(false); return EOF;
0392 }
0393 if(detail::fseek(file_, off, whence) != 0)
0394 return EOF;
0395 return detail::ftell(file_);
0396 }
0397 std::streampos seekpos(std::streampos pos,
0398 std::ios_base::openmode m = std::ios_base::in | std::ios_base::out) override
0399 {
0400
0401 return seekoff(pos, std::ios_base::beg, m);
0402 }
0403 void imbue(const std::locale& loc) override
0404 {
0405 validate_cvt(loc);
0406 }
0407
0408 private:
0409 void make_buffer()
0410 {
0411 if(buffer_)
0412 return;
0413 if(buffer_size_ > 0)
0414 {
0415 buffer_ = new char[buffer_size_];
0416 owns_buffer_ = true;
0417 }
0418 }
0419
0420 void set_unbuffered_read()
0421 {
0422
0423
0424
0425
0426 unbuffered_read_ = !(mode_ & std::ios_base::binary) || buffer_size_ == 0u;
0427 }
0428
0429 void validate_cvt(const std::locale& loc)
0430 {
0431 if(!std::use_facet<std::codecvt<char, char, std::mbstate_t>>(loc).always_noconv())
0432 throw std::runtime_error("Converting codecvts are not supported");
0433 }
0434
0435
0436
0437 bool stop_reading()
0438 {
0439 if(!gptr())
0440 return true;
0441 const auto off = gptr() - egptr();
0442 setg(nullptr, nullptr, nullptr);
0443 if(!off)
0444 return true;
0445 #if defined(__clang__)
0446 #pragma clang diagnostic push
0447 #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
0448 #endif
0449
0450 if(off < std::numeric_limits<std::streamoff>::min())
0451 return false;
0452 #if defined(__clang__)
0453 #pragma clang diagnostic pop
0454 #endif
0455 return detail::fseek(file_, static_cast<std::streamoff>(off), SEEK_CUR) == 0;
0456 }
0457
0458
0459
0460 bool stop_writing()
0461 {
0462 if(pptr())
0463 {
0464 const char* const base = pbase();
0465 const size_t n = pptr() - base;
0466 setp(nullptr, nullptr);
0467 if(n && std::fwrite(base, 1, n, file_) != n)
0468 return false;
0469 }
0470 return true;
0471 }
0472
0473 static const wchar_t* get_mode(std::ios_base::openmode mode)
0474 {
0475
0476
0477
0478
0479
0480
0481 if(mode == (std::ios_base::out))
0482 return L"w";
0483 if(mode == (std::ios_base::out | std::ios_base::app))
0484 return L"a";
0485 if(mode == (std::ios_base::app))
0486 return L"a";
0487 if(mode == (std::ios_base::out | std::ios_base::trunc))
0488 return L"w";
0489 if(mode == (std::ios_base::in))
0490 return L"r";
0491 if(mode == (std::ios_base::in | std::ios_base::out))
0492 return L"r+";
0493 if(mode == (std::ios_base::in | std::ios_base::out | std::ios_base::trunc))
0494 return L"w+";
0495 if(mode == (std::ios_base::in | std::ios_base::out | std::ios_base::app))
0496 return L"a+";
0497 if(mode == (std::ios_base::in | std::ios_base::app))
0498 return L"a+";
0499 if(mode == (std::ios_base::binary | std::ios_base::out))
0500 return L"wb";
0501 if(mode == (std::ios_base::binary | std::ios_base::out | std::ios_base::app))
0502 return L"ab";
0503 if(mode == (std::ios_base::binary | std::ios_base::app))
0504 return L"ab";
0505 if(mode == (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc))
0506 return L"wb";
0507 if(mode == (std::ios_base::binary | std::ios_base::in))
0508 return L"rb";
0509 if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out))
0510 return L"r+b";
0511 if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::trunc))
0512 return L"w+b";
0513 if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::app))
0514 return L"a+b";
0515 if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::app))
0516 return L"a+b";
0517 return nullptr;
0518 }
0519
0520 FILE* file_;
0521 char* buffer_;
0522 size_t buffer_size_;
0523 bool owns_buffer_;
0524 bool unbuffered_read_;
0525 char last_char_[1];
0526 std::ios::openmode mode_;
0527 };
0528
0529
0530
0531
0532 using filebuf = basic_filebuf<char>;
0533
0534
0535 template<typename CharType, typename Traits>
0536 void swap(basic_filebuf<CharType, Traits>& lhs, basic_filebuf<CharType, Traits>& rhs)
0537 {
0538 lhs.swap(rhs);
0539 }
0540
0541 #endif
0542
0543 }
0544 }
0545
0546 #endif