filebuf.hpp 15 KB

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