From 182bff76382d59f44f0ac3c69beb55dacde75eb5 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Fri, 16 Oct 2020 18:13:24 +0300 Subject: add path.hpp: CanonicalPath class, etc. Apparently, relative paths in the Win32 namespace (e.g. \\?\test.txt) are not a thing. --- include/winapi/file.hpp | 3 +++ include/winapi/path.hpp | 25 +++++++++++++++++++++++++ include/winapi/stream.hpp | 4 ++++ src/file.cpp | 27 +++++++++++++++++++++------ src/path.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/stream.cpp | 7 +++++++ 6 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 include/winapi/path.hpp create mode 100644 src/path.cpp diff --git a/include/winapi/file.hpp b/include/winapi/file.hpp index 60a8f1d..221047b 100644 --- a/include/winapi/file.hpp +++ b/include/winapi/file.hpp @@ -6,6 +6,7 @@ #pragma once #include "handle.hpp" +#include "path.hpp" #include #include @@ -17,7 +18,9 @@ public: explicit File(Handle&& handle) : Handle{std::move(handle)} {} static Handle open_for_reading(const std::string&); + static Handle open_for_reading(const CanonicalPath&); static Handle open_for_writing(const std::string&); + static Handle open_for_writing(const CanonicalPath&); using Handle::close; diff --git a/include/winapi/path.hpp b/include/winapi/path.hpp new file mode 100644 index 0000000..f2b0aa9 --- /dev/null +++ b/include/winapi/path.hpp @@ -0,0 +1,25 @@ +// Copyright (c) 2020 Egor Tensin +// This file is part of the "winapi-common" project. +// For details, see https://github.com/egor-tensin/winapi-common. +// Distributed under the MIT License. + +#pragma once + +#include + +namespace winapi { + +class CanonicalPath { +public: + static std::string canonicalize(const std::string&); + + explicit CanonicalPath(const std::string&); + + std::string get() const { return m_path; } + std::string path() const { return get(); } + +private: + std::string m_path; +}; + +} // namespace winapi diff --git a/include/winapi/stream.hpp b/include/winapi/stream.hpp index 6b4bc38..ed5b40c 100644 --- a/include/winapi/stream.hpp +++ b/include/winapi/stream.hpp @@ -6,6 +6,7 @@ #pragma once #include "handle.hpp" +#include "path.hpp" #include "pipe.hpp" #include @@ -45,6 +46,7 @@ inline void swap(Stream& a, Stream& b) BOOST_NOEXCEPT_OR_NOTHROW { struct Stdin : Stream { Stdin(); explicit Stdin(const std::string& file); + explicit Stdin(const CanonicalPath& file); explicit Stdin(Pipe&); // VS 2013 won't generate these automatically. @@ -62,6 +64,7 @@ struct Stdin : Stream { struct Stdout : Stream { Stdout(); explicit Stdout(const std::string& file); + explicit Stdout(const CanonicalPath& file); explicit Stdout(Pipe&); // VS 2013 won't generate these automatically. @@ -79,6 +82,7 @@ struct Stdout : Stream { struct Stderr : Stream { Stderr(); explicit Stderr(const std::string& file); + explicit Stderr(const CanonicalPath& file); explicit Stderr(Pipe&); // VS 2013 won't generate these automatically. diff --git a/src/file.cpp b/src/file.cpp index cb9be81..a299d70 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -14,6 +15,14 @@ namespace winapi { namespace { +std::wstring to_system_path(const std::string& path) { + return widen(path); +} + +std::wstring to_system_path(const CanonicalPath& path) { + return widen(R"(\\?\)" + path.get()); +} + struct CreateFileParams { static CreateFileParams read() { CreateFileParams params; @@ -39,15 +48,13 @@ private: CreateFileParams() = default; }; -Handle open_file(const std::string& path, const CreateFileParams& params) { - const auto unicode_path = LR"(\\?\)" + widen(path); - +Handle open_file(const std::wstring& path, const CreateFileParams& params) { SECURITY_ATTRIBUTES attributes; std::memset(&attributes, 0, sizeof(attributes)); attributes.nLength = sizeof(attributes); attributes.bInheritHandle = TRUE; - const auto handle = ::CreateFileW(unicode_path.c_str(), + const auto handle = ::CreateFileW(path.c_str(), params.dwDesiredAccess, params.dwShareMode, &attributes, @@ -65,11 +72,19 @@ Handle open_file(const std::string& path, const CreateFileParams& params) { } // namespace Handle File::open_for_reading(const std::string& path) { - return open_file(path, CreateFileParams::read()); + return open_file(to_system_path(path), CreateFileParams::read()); +} + +Handle File::open_for_reading(const CanonicalPath& path) { + return open_file(to_system_path(path), CreateFileParams::read()); } Handle File::open_for_writing(const std::string& path) { - return open_file(path, CreateFileParams::write()); + return open_file(to_system_path(path), CreateFileParams::write()); +} + +Handle File::open_for_writing(const CanonicalPath& path) { + return open_file(to_system_path(path), CreateFileParams::write()); } } // namespace winapi diff --git a/src/path.cpp b/src/path.cpp new file mode 100644 index 0000000..2c2deda --- /dev/null +++ b/src/path.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2020 Egor Tensin +// This file is part of the "winapi-common" project. +// For details, see https://github.com/egor-tensin/winapi-common. +// Distributed under the MIT License. + +#include +#include +#include + +#include + +#include +#include + +namespace winapi { +namespace { + +std::wstring do_canonicalize(const std::wstring& path) { + std::vector buffer; + buffer.resize(MAX_PATH); + + while (true) { + const auto nch = ::GetFullPathNameW(path.c_str(), buffer.size(), buffer.data(), NULL); + + if (nch == 0) { + throw error::windows(GetLastError(), "GetFullPathNameW"); + } + + if (nch < buffer.size()) { + return {buffer.data(), nch}; + } + + if (nch > buffer.size()) { + buffer.resize(2 * buffer.size()); + } + } +} + +} // namespace + +CanonicalPath::CanonicalPath(const std::string& path) : m_path(canonicalize(path)) {} + +std::string CanonicalPath::canonicalize(const std::string& path) { + return narrow(do_canonicalize(widen(path))); +} + +} // namespace winapi diff --git a/src/stream.cpp b/src/stream.cpp index b31635e..f9285e6 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -21,10 +22,16 @@ Stderr::Stderr() : Stream{Handle::std_err()} {} Stdin::Stdin(const std::string& path) : Stream{File::open_for_reading(path)} {} +Stdin::Stdin(const CanonicalPath& path) : Stream{File::open_for_reading(path)} {} + Stdout::Stdout(const std::string& path) : Stream{File::open_for_writing(path)} {} +Stdout::Stdout(const CanonicalPath& path) : Stream{File::open_for_writing(path)} {} + Stderr::Stderr(const std::string& path) : Stream{File::open_for_writing(path)} {} +Stderr::Stderr(const CanonicalPath& path) : Stream{File::open_for_writing(path)} {} + Stdin::Stdin(Pipe& pipe) : Stream{std::move(pipe.read_end())} { pipe.write_end().dont_inherit(); } -- cgit v1.2.3