winapi_common
path.cpp
1 // Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
2 // This file is part of the "winapi-common" project.
3 // For details, see https://github.com/egor-tensin/winapi-common.
4 // Distributed under the MIT License.
5 
6 #include <winapi/error.hpp>
7 #include <winapi/path.hpp>
8 #include <winapi/utf8.hpp>
9 
10 #include <windows.h>
11 
12 #include <cstddef>
13 #include <limits>
14 #include <stdexcept>
15 #include <string>
16 #include <vector>
17 
18 namespace winapi {
19 namespace {
20 
21 std::wstring do_canonicalize(const std::wstring& path) {
22  static constexpr std::size_t init_buffer_size = MAX_PATH;
23  static_assert(init_buffer_size > 0, "init_buffer_size must be positive");
24 
25  std::vector<wchar_t> buffer;
26  buffer.resize(init_buffer_size);
27 
28  while (true) {
29  if (buffer.size() > std::numeric_limits<DWORD>::max())
30  throw std::range_error{"Path buffer is too large"};
31  const auto nch = ::GetFullPathNameW(
32  path.c_str(), static_cast<DWORD>(buffer.size()), buffer.data(), NULL);
33 
34  if (nch == 0) {
35  throw error::windows(GetLastError(), "GetFullPathNameW");
36  }
37 
38  if (nch < buffer.size()) {
39  return {buffer.data(), nch};
40  }
41 
42  if (nch > buffer.size()) {
43  buffer.resize(2 * buffer.size());
44  }
45  }
46 }
47 
48 } // namespace
49 
50 CanonicalPath::CanonicalPath(const std::string& path) : m_path(canonicalize(path)) {}
51 
52 std::string CanonicalPath::canonicalize(const std::string& path) {
53  return narrow(do_canonicalize(widen(path)));
54 }
55 
56 } // namespace winapi
Make std::system_error work with GetLastError().