1  
//
1  
//
2  
// Copyright (c) 2022 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2022 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/http
7  
// Official repository: https://github.com/cppalliance/http
8  
//
8  
//
9  

9  

10  
#include "src/detail/win32_unicode_path.hpp"
10  
#include "src/detail/win32_unicode_path.hpp"
11  
#include <boost/http/detail/file_stdio.hpp>
11  
#include <boost/http/detail/file_stdio.hpp>
12  
#include <boost/http/error.hpp>
12  
#include <boost/http/error.hpp>
13  
#include <boost/system/errc.hpp>
13  
#include <boost/system/errc.hpp>
14  
#include <boost/config/workaround.hpp>
14  
#include <boost/config/workaround.hpp>
15  
#include <boost/core/exchange.hpp>
15  
#include <boost/core/exchange.hpp>
16  
#include <limits>
16  
#include <limits>
17  

17  

18  
namespace boost {
18  
namespace boost {
19  
namespace http {
19  
namespace http {
20  
namespace detail {
20  
namespace detail {
21  

21  

22  
file_stdio::
22  
file_stdio::
23  
~file_stdio()
23  
~file_stdio()
24  
{
24  
{
25  
    if(f_)
25  
    if(f_)
26  
        fclose(f_);
26  
        fclose(f_);
27  
}
27  
}
28  

28  

29  
file_stdio::
29  
file_stdio::
30  
file_stdio(
30  
file_stdio(
31  
    file_stdio&& other) noexcept
31  
    file_stdio&& other) noexcept
32  
    : f_(boost::exchange(other.f_, nullptr))
32  
    : f_(boost::exchange(other.f_, nullptr))
33  
{
33  
{
34  
}
34  
}
35  

35  

36  
file_stdio&
36  
file_stdio&
37  
file_stdio::
37  
file_stdio::
38  
operator=(
38  
operator=(
39  
    file_stdio&& other) noexcept
39  
    file_stdio&& other) noexcept
40  
{
40  
{
41  
    if(&other == this)
41  
    if(&other == this)
42  
        return *this;
42  
        return *this;
43  
    if(f_)
43  
    if(f_)
44  
        fclose(f_);
44  
        fclose(f_);
45  
    f_ = other.f_;
45  
    f_ = other.f_;
46  
    other.f_ = nullptr;
46  
    other.f_ = nullptr;
47  
    return *this;
47  
    return *this;
48  
}
48  
}
49  

49  

50  
void
50  
void
51  
file_stdio::
51  
file_stdio::
52  
native_handle(std::FILE* f)
52  
native_handle(std::FILE* f)
53  
{
53  
{
54  
    if(f_)
54  
    if(f_)
55  
        fclose(f_);
55  
        fclose(f_);
56  
    f_ = f;
56  
    f_ = f;
57  
}
57  
}
58  

58  

59  
void
59  
void
60  
file_stdio::
60  
file_stdio::
61  
close(
61  
close(
62  
    system::error_code& ec)
62  
    system::error_code& ec)
63  
{
63  
{
64  
    if(f_)
64  
    if(f_)
65  
    {
65  
    {
66  
        int failed = fclose(f_);
66  
        int failed = fclose(f_);
67  
        f_ = nullptr;
67  
        f_ = nullptr;
68  
        if(failed)
68  
        if(failed)
69  
        {
69  
        {
70  
            ec.assign(errno,
70  
            ec.assign(errno,
71  
                system::generic_category());
71  
                system::generic_category());
72  
            return;
72  
            return;
73  
        }
73  
        }
74  
    }
74  
    }
75  
    ec = {};
75  
    ec = {};
76  
}
76  
}
77  

77  

78  
void
78  
void
79  
file_stdio::
79  
file_stdio::
80  
open(char const* path, file_mode mode,
80  
open(char const* path, file_mode mode,
81  
    system::error_code& ec)
81  
    system::error_code& ec)
82  
{
82  
{
83  
    if(f_)
83  
    if(f_)
84  
    {
84  
    {
85  
        fclose(f_);
85  
        fclose(f_);
86  
        f_ = nullptr;
86  
        f_ = nullptr;
87  
    }
87  
    }
88  
    ec = {};
88  
    ec = {};
89  
#ifdef _WIN32
89  
#ifdef _WIN32
90  
    boost::winapi::WCHAR_ const* s;
90  
    boost::winapi::WCHAR_ const* s;
91  
    detail::win32_unicode_path unicode_path(path, ec);
91  
    detail::win32_unicode_path unicode_path(path, ec);
92  
    if (ec)
92  
    if (ec)
93  
        return;
93  
        return;
94  
#else
94  
#else
95  
    char const* s;
95  
    char const* s;
96  
#endif
96  
#endif
97  
    switch(mode)
97  
    switch(mode)
98  
    {
98  
    {
99  
    default:
99  
    default:
100  
    case file_mode::read:
100  
    case file_mode::read:
101  
    #ifdef _WIN32
101  
    #ifdef _WIN32
102  
        s = L"rb";
102  
        s = L"rb";
103  
    #else
103  
    #else
104  
        s = "rb";
104  
        s = "rb";
105  
    #endif
105  
    #endif
106  
        break;
106  
        break;
107  

107  

108  
    case file_mode::scan:
108  
    case file_mode::scan:
109  
    #ifdef _WIN32
109  
    #ifdef _WIN32
110  
        s = L"rbS";
110  
        s = L"rbS";
111  
    #else
111  
    #else
112  
        s = "rb";
112  
        s = "rb";
113  
    #endif
113  
    #endif
114  
        break;
114  
        break;
115  

115  

116  
    case file_mode::write:
116  
    case file_mode::write:
117  
    #ifdef _WIN32
117  
    #ifdef _WIN32
118  
        s = L"wb+";
118  
        s = L"wb+";
119  
    #else
119  
    #else
120  
        s = "wb+";
120  
        s = "wb+";
121  
    #endif
121  
    #endif
122  
        break;
122  
        break;
123  

123  

124  
    case file_mode::write_new:
124  
    case file_mode::write_new:
125  
    {
125  
    {
126  
#ifdef _WIN32
126  
#ifdef _WIN32
127  
# if (defined(BOOST_MSVC) && BOOST_MSVC >= 1910) || (defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION >= 141)
127  
# if (defined(BOOST_MSVC) && BOOST_MSVC >= 1910) || (defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION >= 141)
128  
        s = L"wbx";
128  
        s = L"wbx";
129  
# else
129  
# else
130  
        std::FILE* f0;
130  
        std::FILE* f0;
131  
        auto const ev = ::_wfopen_s(&f0, unicode_path.c_str(), L"rb");
131  
        auto const ev = ::_wfopen_s(&f0, unicode_path.c_str(), L"rb");
132  
        if(! ev)
132  
        if(! ev)
133  
        {
133  
        {
134  
            std::fclose(f0);
134  
            std::fclose(f0);
135  
            ec = make_error_code(
135  
            ec = make_error_code(
136  
                system::errc::file_exists);
136  
                system::errc::file_exists);
137  
            return;
137  
            return;
138  
        }
138  
        }
139  
        else if(ev !=
139  
        else if(ev !=
140  
            system::errc::no_such_file_or_directory)
140  
            system::errc::no_such_file_or_directory)
141  
        {
141  
        {
142  
            ec.assign(ev,
142  
            ec.assign(ev,
143  
                system::generic_category());
143  
                system::generic_category());
144  
            return;
144  
            return;
145  
        }
145  
        }
146  
        s = L"wb";
146  
        s = L"wb";
147  
# endif
147  
# endif
148  
#else
148  
#else
149  
        s = "wbx";
149  
        s = "wbx";
150  
#endif
150  
#endif
151  
        break;
151  
        break;
152  
    }
152  
    }
153  

153  

154  
    case file_mode::write_existing:
154  
    case file_mode::write_existing:
155  
    #ifdef _WIN32
155  
    #ifdef _WIN32
156  
        s = L"rb+";
156  
        s = L"rb+";
157  
    #else
157  
    #else
158  
        s = "rb+";
158  
        s = "rb+";
159  
    #endif
159  
    #endif
160  
        break;
160  
        break;
161  

161  

162  
    case file_mode::append:
162  
    case file_mode::append:
163  
    #ifdef _WIN32
163  
    #ifdef _WIN32
164  
        s = L"ab";
164  
        s = L"ab";
165  
    #else
165  
    #else
166  
        s = "ab";
166  
        s = "ab";
167  
    #endif
167  
    #endif
168  
        break;
168  
        break;
169  

169  

170  
    case file_mode::append_existing:
170  
    case file_mode::append_existing:
171  
    {
171  
    {
172  
#ifdef _WIN32
172  
#ifdef _WIN32
173  
        std::FILE* f0;
173  
        std::FILE* f0;
174  
        auto const ev =
174  
        auto const ev =
175  
            ::_wfopen_s(&f0, unicode_path.c_str(), L"rb+");
175  
            ::_wfopen_s(&f0, unicode_path.c_str(), L"rb+");
176  
        if(ev)
176  
        if(ev)
177  
        {
177  
        {
178  
            ec.assign(ev,
178  
            ec.assign(ev,
179  
                system::generic_category());
179  
                system::generic_category());
180  
            return;
180  
            return;
181  
        }
181  
        }
182  
#else
182  
#else
183  
        auto const f0 =
183  
        auto const f0 =
184  
            std::fopen(path, "rb+");
184  
            std::fopen(path, "rb+");
185  
        if(! f0)
185  
        if(! f0)
186  
        {
186  
        {
187  
            ec.assign(errno,
187  
            ec.assign(errno,
188  
                system::generic_category());
188  
                system::generic_category());
189  
            return;
189  
            return;
190  
        }
190  
        }
191  
#endif
191  
#endif
192  
        std::fclose(f0);
192  
        std::fclose(f0);
193  
    #ifdef _WIN32
193  
    #ifdef _WIN32
194  
        s = L"ab";
194  
        s = L"ab";
195  
    #else
195  
    #else
196  
        s = "ab";
196  
        s = "ab";
197  
    #endif
197  
    #endif
198  
        break;
198  
        break;
199  
    }
199  
    }
200  
    }
200  
    }
201  

201  

202  
#ifdef _WIN32
202  
#ifdef _WIN32
203  
    auto const ev = ::_wfopen_s(
203  
    auto const ev = ::_wfopen_s(
204  
        &f_, unicode_path.c_str(), s);
204  
        &f_, unicode_path.c_str(), s);
205  
    if(ev)
205  
    if(ev)
206  
    {
206  
    {
207  
        f_ = nullptr;
207  
        f_ = nullptr;
208  
        ec.assign(ev,
208  
        ec.assign(ev,
209  
            system::generic_category());
209  
            system::generic_category());
210  
        return;
210  
        return;
211  
    }
211  
    }
212  
#else
212  
#else
213  
    f_ = std::fopen(path, s);
213  
    f_ = std::fopen(path, s);
214  
    if(! f_)
214  
    if(! f_)
215  
    {
215  
    {
216  
        ec.assign(errno,
216  
        ec.assign(errno,
217  
            system::generic_category());
217  
            system::generic_category());
218  
        return;
218  
        return;
219  
    }
219  
    }
220  
#endif
220  
#endif
221  
}
221  
}
222  

222  

223  
std::uint64_t
223  
std::uint64_t
224  
file_stdio::
224  
file_stdio::
225  
size(
225  
size(
226  
    system::error_code& ec) const
226  
    system::error_code& ec) const
227  
{
227  
{
228  
    if(! f_)
228  
    if(! f_)
229  
    {
229  
    {
230  
        ec = make_error_code(
230  
        ec = make_error_code(
231  
            system::errc::bad_file_descriptor);
231  
            system::errc::bad_file_descriptor);
232  
        return 0;
232  
        return 0;
233  
    }
233  
    }
234  
    long pos = std::ftell(f_);
234  
    long pos = std::ftell(f_);
235  
    if(pos == -1L)
235  
    if(pos == -1L)
236  
    {
236  
    {
237  
        ec.assign(errno,
237  
        ec.assign(errno,
238  
            system::generic_category());
238  
            system::generic_category());
239  
        return 0;
239  
        return 0;
240  
    }
240  
    }
241  
    int result = std::fseek(f_, 0, SEEK_END);
241  
    int result = std::fseek(f_, 0, SEEK_END);
242  
    if(result != 0)
242  
    if(result != 0)
243  
    {
243  
    {
244  
        ec.assign(errno,
244  
        ec.assign(errno,
245  
            system::generic_category());
245  
            system::generic_category());
246  
        return 0;
246  
        return 0;
247  
    }
247  
    }
248  
    long size = std::ftell(f_);
248  
    long size = std::ftell(f_);
249  
    if(size == -1L)
249  
    if(size == -1L)
250  
    {
250  
    {
251  
        ec.assign(errno,
251  
        ec.assign(errno,
252  
            system::generic_category());
252  
            system::generic_category());
253  
        std::fseek(f_, pos, SEEK_SET);
253  
        std::fseek(f_, pos, SEEK_SET);
254  
        return 0;
254  
        return 0;
255  
    }
255  
    }
256  
    result = std::fseek(f_, pos, SEEK_SET);
256  
    result = std::fseek(f_, pos, SEEK_SET);
257  
    if(result != 0)
257  
    if(result != 0)
258  
        ec.assign(errno,
258  
        ec.assign(errno,
259  
            system::generic_category());
259  
            system::generic_category());
260  
    else
260  
    else
261  
        ec = {};
261  
        ec = {};
262  
    return size;
262  
    return size;
263  
}
263  
}
264  

264  

265  
std::uint64_t
265  
std::uint64_t
266  
file_stdio::
266  
file_stdio::
267  
pos(
267  
pos(
268  
    system::error_code& ec) const
268  
    system::error_code& ec) const
269  
{
269  
{
270  
    if(! f_)
270  
    if(! f_)
271  
    {
271  
    {
272  
        ec = make_error_code(
272  
        ec = make_error_code(
273  
            system::errc::bad_file_descriptor);
273  
            system::errc::bad_file_descriptor);
274  
        return 0;
274  
        return 0;
275  
    }
275  
    }
276  
    long pos = std::ftell(f_);
276  
    long pos = std::ftell(f_);
277  
    if(pos == -1L)
277  
    if(pos == -1L)
278  
    {
278  
    {
279  
        ec.assign(errno,
279  
        ec.assign(errno,
280  
            system::generic_category());
280  
            system::generic_category());
281  
        return 0;
281  
        return 0;
282  
    }
282  
    }
283  
    ec = {};
283  
    ec = {};
284  
    return pos;
284  
    return pos;
285  
}
285  
}
286  

286  

287  
void
287  
void
288  
file_stdio::
288  
file_stdio::
289  
seek(std::uint64_t offset,
289  
seek(std::uint64_t offset,
290  
    system::error_code& ec)
290  
    system::error_code& ec)
291  
{
291  
{
292  
    if(! f_)
292  
    if(! f_)
293  
    {
293  
    {
294  
        ec = make_error_code(
294  
        ec = make_error_code(
295  
            system::errc::bad_file_descriptor);
295  
            system::errc::bad_file_descriptor);
296  
        return;
296  
        return;
297  
    }
297  
    }
298  
    if(offset > static_cast<std::uint64_t>((std::numeric_limits<long>::max)()))
298  
    if(offset > static_cast<std::uint64_t>((std::numeric_limits<long>::max)()))
299  
    {
299  
    {
300  
        ec = make_error_code(
300  
        ec = make_error_code(
301  
            system::errc::invalid_seek);
301  
            system::errc::invalid_seek);
302  
        return;
302  
        return;
303  
    }
303  
    }
304  
    int result = std::fseek(f_,
304  
    int result = std::fseek(f_,
305  
        static_cast<long>(offset), SEEK_SET);
305  
        static_cast<long>(offset), SEEK_SET);
306  
    if(result != 0)
306  
    if(result != 0)
307  
        ec.assign(errno,
307  
        ec.assign(errno,
308  
            system::generic_category());
308  
            system::generic_category());
309  
    else
309  
    else
310  
        ec = {};
310  
        ec = {};
311  
}
311  
}
312  

312  

313  
std::size_t
313  
std::size_t
314  
file_stdio::
314  
file_stdio::
315  
read(void* buffer, std::size_t n,
315  
read(void* buffer, std::size_t n,
316  
    system::error_code& ec)
316  
    system::error_code& ec)
317  
{
317  
{
318  
    if(! f_)
318  
    if(! f_)
319  
    {
319  
    {
320  
        ec = make_error_code(
320  
        ec = make_error_code(
321  
            system::errc::bad_file_descriptor);
321  
            system::errc::bad_file_descriptor);
322  
        return 0;
322  
        return 0;
323  
    }
323  
    }
324  
    auto nread = std::fread(buffer, 1, n, f_);
324  
    auto nread = std::fread(buffer, 1, n, f_);
325  
    if(std::ferror(f_))
325  
    if(std::ferror(f_))
326  
    {
326  
    {
327  
        ec.assign(errno,
327  
        ec.assign(errno,
328  
            system::generic_category());
328  
            system::generic_category());
329  
        return 0;
329  
        return 0;
330  
    }
330  
    }
331  
    return nread;
331  
    return nread;
332  
}
332  
}
333  

333  

334  
std::size_t
334  
std::size_t
335  
file_stdio::
335  
file_stdio::
336  
write(void const* buffer, std::size_t n,
336  
write(void const* buffer, std::size_t n,
337  
    system::error_code& ec)
337  
    system::error_code& ec)
338  
{
338  
{
339  
    if(! f_)
339  
    if(! f_)
340  
    {
340  
    {
341  
        ec = make_error_code(
341  
        ec = make_error_code(
342  
            system::errc::bad_file_descriptor);
342  
            system::errc::bad_file_descriptor);
343  
        return 0;
343  
        return 0;
344  
    }
344  
    }
345  
    auto nwritten = std::fwrite(buffer, 1, n, f_);
345  
    auto nwritten = std::fwrite(buffer, 1, n, f_);
346  
    if(std::ferror(f_))
346  
    if(std::ferror(f_))
347  
    {
347  
    {
348  
        ec.assign(errno,
348  
        ec.assign(errno,
349  
            system::generic_category());
349  
            system::generic_category());
350  
        return 0;
350  
        return 0;
351  
    }
351  
    }
352  
    return nwritten;
352  
    return nwritten;
353  
}
353  
}
354  

354  

355  
} // detail
355  
} // detail
356  
} // http
356  
} // http
357  
} // boost
357  
} // boost