1//
2// Copyright (c) 2015-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7// Official repository: https://github.com/boostorg/beast
8//
9
10#ifndef BOOST_BEAST_CORE_IMPL_FILE_STDIO_IPP
11#define BOOST_BEAST_CORE_IMPL_FILE_STDIO_IPP
12
13#include <boost/beast/core/file_stdio.hpp>
14#include <boost/config/workaround.hpp>
15#include <boost/core/exchange.hpp>
16#include <limits>
17
18namespace boost {
19namespace beast {
20
21file_stdio::
22~file_stdio()
23{
24    if(f_)
25        fclose(f_);
26}
27
28file_stdio::
29file_stdio(file_stdio&& other)
30    : f_(boost::exchange(other.f_, nullptr))
31{
32}
33
34file_stdio&
35file_stdio::
36operator=(file_stdio&& other)
37{
38    if(&other == this)
39        return *this;
40    if(f_)
41        fclose(f_);
42    f_ = other.f_;
43    other.f_ = nullptr;
44    return *this;
45}
46
47void
48file_stdio::
49native_handle(FILE* f)
50{
51    if(f_)
52        fclose(f_);
53    f_ = f;
54}
55
56void
57file_stdio::
58close(error_code& ec)
59{
60    if(f_)
61    {
62        int failed = fclose(f_);
63        f_ = nullptr;
64        if(failed)
65        {
66            ec.assign(errno, generic_category());
67            return;
68        }
69    }
70    ec = {};
71}
72
73void
74file_stdio::
75open(char const* path, file_mode mode, error_code& ec)
76{
77    if(f_)
78    {
79        fclose(f_);
80        f_ = nullptr;
81    }
82    char const* s;
83    switch(mode)
84    {
85    default:
86    case file_mode::read:
87        s = "rb";
88        break;
89
90    case file_mode::scan:
91    #ifdef BOOST_MSVC
92        s = "rbS";
93    #else
94        s = "rb";
95    #endif
96        break;
97
98    case file_mode::write:
99        s = "wb+";
100        break;
101
102    case file_mode::write_new:
103    {
104#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
105        FILE* f0;
106        auto const ev = ::fopen_s(&f0, path, "rb");
107        if(! ev)
108        {
109            std::fclose(f0);
110            ec = make_error_code(errc::file_exists);
111            return;
112        }
113        else if(ev !=
114            errc::no_such_file_or_directory)
115        {
116            ec.assign(ev, generic_category());
117            return;
118        }
119        s = "wb";
120#else
121
122        s = "wbx";
123#endif
124        break;
125    }
126
127    case file_mode::write_existing:
128        s = "rb+";
129        break;
130
131    case file_mode::append:
132        s = "ab";
133        break;
134
135    case file_mode::append_existing:
136    {
137#ifdef BOOST_MSVC
138        FILE* f0;
139        auto const ev =
140            ::fopen_s(&f0, path, "rb+");
141        if(ev)
142        {
143            ec.assign(ev, generic_category());
144            return;
145        }
146#else
147        auto const f0 =
148            std::fopen(path, "rb+");
149        if(! f0)
150        {
151            ec.assign(errno, generic_category());
152            return;
153        }
154#endif
155        std::fclose(f0);
156        s = "ab";
157        break;
158    }
159    }
160
161#ifdef BOOST_MSVC
162    auto const ev = ::fopen_s(&f_, path, s);
163    if(ev)
164    {
165        f_ = nullptr;
166        ec.assign(ev, generic_category());
167        return;
168    }
169#else
170    f_ = std::fopen(path, s);
171    if(! f_)
172    {
173        ec.assign(errno, generic_category());
174        return;
175    }
176#endif
177    ec = {};
178}
179
180std::uint64_t
181file_stdio::
182size(error_code& ec) const
183{
184    if(! f_)
185    {
186        ec = make_error_code(errc::bad_file_descriptor);
187        return 0;
188    }
189    long pos = std::ftell(f_);
190    if(pos == -1L)
191    {
192        ec.assign(errno, generic_category());
193        return 0;
194    }
195    int result = std::fseek(f_, 0, SEEK_END);
196    if(result != 0)
197    {
198        ec.assign(errno, generic_category());
199        return 0;
200    }
201    long size = std::ftell(f_);
202    if(size == -1L)
203    {
204        ec.assign(errno, generic_category());
205        std::fseek(f_, pos, SEEK_SET);
206        return 0;
207    }
208    result = std::fseek(f_, pos, SEEK_SET);
209    if(result != 0)
210        ec.assign(errno, generic_category());
211    else
212        ec = {};
213    return size;
214}
215
216std::uint64_t
217file_stdio::
218pos(error_code& ec) const
219{
220    if(! f_)
221    {
222        ec = make_error_code(errc::bad_file_descriptor);
223        return 0;
224    }
225    long pos = std::ftell(f_);
226    if(pos == -1L)
227    {
228        ec.assign(errno, generic_category());
229        return 0;
230    }
231    ec = {};
232    return pos;
233}
234
235void
236file_stdio::
237seek(std::uint64_t offset, error_code& ec)
238{
239    if(! f_)
240    {
241        ec = make_error_code(errc::bad_file_descriptor);
242        return;
243    }
244    if(offset > static_cast<std::uint64_t>(std::numeric_limits<long>::max()))
245    {
246        ec = make_error_code(errc::invalid_seek);
247        return;
248    }
249    int result = std::fseek(f_,
250        static_cast<long>(offset), SEEK_SET);
251    if(result != 0)
252        ec.assign(errno, generic_category());
253    else
254        ec = {};
255}
256
257std::size_t
258file_stdio::
259read(void* buffer, std::size_t n, error_code& ec) const
260{
261    if(! f_)
262    {
263        ec = make_error_code(errc::bad_file_descriptor);
264        return 0;
265    }
266    auto nread = std::fread(buffer, 1, n, f_);
267    if(std::ferror(f_))
268    {
269        ec.assign(errno, generic_category());
270        return 0;
271    }
272    return nread;
273}
274
275std::size_t
276file_stdio::
277write(void const* buffer, std::size_t n, error_code& ec)
278{
279    if(! f_)
280    {
281        ec = make_error_code(errc::bad_file_descriptor);
282        return 0;
283    }
284    auto nwritten = std::fwrite(buffer, 1, n, f_);
285    if(std::ferror(f_))
286    {
287        ec.assign(errno, generic_category());
288        return 0;
289    }
290    return nwritten;
291}
292
293} // beast
294} // boost
295
296#endif
297