parent
414a625a79
commit
3c928f67be
3 changed files with 0 additions and 1284 deletions
@ -1,505 +0,0 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: laszlocsomor@google.com (Laszlo Csomor)
|
||||
//
|
||||
// Implementation for long-path-aware open/mkdir/access/etc. on Windows, as well
|
||||
// as for the supporting utility functions.
|
||||
//
|
||||
// These functions convert the input path to an absolute Windows path
|
||||
// with "\\?\" prefix, then pass that to _wopen/_wmkdir/_waccess/etc.
|
||||
// (declared in <io.h>) respectively. This allows working with files/directories
|
||||
// whose paths are longer than MAX_PATH (260 chars).
|
||||
//
|
||||
// This file is only used on Windows, it's empty on other platforms.
|
||||
|
||||
#if defined(_WIN32) |
||||
|
||||
// Comment this out to fall back to using the ANSI versions (open, mkdir, ...)
|
||||
// instead of the Unicode ones (_wopen, _wmkdir, ...). Doing so can be useful to
|
||||
// debug failing tests if that's caused by the long path support.
|
||||
// #define SUPPORT_LONGPATHS
|
||||
|
||||
#include <ctype.h> |
||||
#include <direct.h> |
||||
#include <errno.h> |
||||
#include <fcntl.h> |
||||
#include <io.h> |
||||
#include <memory> |
||||
#include <sys/stat.h> |
||||
#include <sys/types.h> |
||||
#include <wctype.h> |
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN |
||||
#define WIN32_LEAN_AND_MEAN 1 |
||||
#endif |
||||
|
||||
#include <windows.h> |
||||
|
||||
#include <google/protobuf/stubs/io_win32.h> |
||||
|
||||
#include <memory> |
||||
#include <sstream> |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
namespace internal { |
||||
namespace win32 { |
||||
namespace { |
||||
|
||||
using std::string; |
||||
using std::wstring; |
||||
|
||||
template <typename char_type> |
||||
struct CharTraits { |
||||
static bool is_alpha(char_type ch); |
||||
}; |
||||
|
||||
template <> |
||||
struct CharTraits<char> { |
||||
static bool is_alpha(char ch) { return isalpha(ch); } |
||||
}; |
||||
|
||||
template <> |
||||
struct CharTraits<wchar_t> { |
||||
static bool is_alpha(wchar_t ch) { return iswalpha(ch); } |
||||
}; |
||||
|
||||
template <typename char_type> |
||||
bool null_or_empty(const char_type* s) { |
||||
return s == nullptr || *s == 0; |
||||
} |
||||
|
||||
// Returns true if the path starts with a drive letter, e.g. "c:".
|
||||
// Note that this won't check for the "\" after the drive letter, so this also
|
||||
// returns true for "c:foo" (which is "c:\${PWD}\foo").
|
||||
// This check requires that a path not have a longpath prefix ("\\?\").
|
||||
template <typename char_type> |
||||
bool has_drive_letter(const char_type* ch) { |
||||
return CharTraits<char_type>::is_alpha(ch[0]) && ch[1] == ':'; |
||||
} |
||||
|
||||
// Returns true if the path starts with a longpath prefix ("\\?\").
|
||||
template <typename char_type> |
||||
bool has_longpath_prefix(const char_type* path) { |
||||
return path[0] == '\\' && path[1] == '\\' && path[2] == '?' && |
||||
path[3] == '\\'; |
||||
} |
||||
|
||||
template <typename char_type> |
||||
bool is_separator(char_type c) { |
||||
return c == '/' || c == '\\'; |
||||
} |
||||
|
||||
// Returns true if the path starts with a drive specifier (e.g. "c:\").
|
||||
template <typename char_type> |
||||
bool is_path_absolute(const char_type* path) { |
||||
return has_drive_letter(path) && is_separator(path[2]); |
||||
} |
||||
|
||||
template <typename char_type> |
||||
bool is_drive_relative(const char_type* path) { |
||||
return has_drive_letter(path) && (path[2] == 0 || !is_separator(path[2])); |
||||
} |
||||
|
||||
wstring join_paths(const wstring& path1, const wstring& path2) { |
||||
if (path1.empty() || is_path_absolute(path2.c_str()) || |
||||
has_longpath_prefix(path2.c_str())) { |
||||
return path2; |
||||
} |
||||
if (path2.empty()) { |
||||
return path1; |
||||
} |
||||
|
||||
if (is_separator(path1[path1.size() - 1])) { |
||||
return is_separator(path2[0]) ? (path1 + path2.substr(1)) |
||||
: (path1 + path2); |
||||
} else { |
||||
return is_separator(path2[0]) ? (path1 + path2) |
||||
: (path1 + L'\\' + path2); |
||||
} |
||||
} |
||||
|
||||
wstring normalize(wstring path) { |
||||
if (has_longpath_prefix(path.c_str())) { |
||||
path = path.substr(4); |
||||
} |
||||
|
||||
static const wstring dot(L"."); |
||||
static const wstring dotdot(L".."); |
||||
const WCHAR* p = path.c_str(); |
||||
|
||||
std::vector<wstring> segments; |
||||
int segment_start = -1; |
||||
// Find the path segments in `path` (separated by "/").
|
||||
for (int i = 0;; ++i) { |
||||
if (!is_separator(p[i]) && p[i] != L'\0') { |
||||
// The current character does not end a segment, so start one unless it's
|
||||
// already started.
|
||||
if (segment_start < 0) { |
||||
segment_start = i; |
||||
} |
||||
} else if (segment_start >= 0 && i > segment_start) { |
||||
// The current character is "/" or "\0", so this ends a segment.
|
||||
// Add that to `segments` if there's anything to add; handle "." and "..".
|
||||
wstring segment(p, segment_start, i - segment_start); |
||||
segment_start = -1; |
||||
if (segment == dotdot) { |
||||
if (!segments.empty() && |
||||
(!has_drive_letter(segments[0].c_str()) || segments.size() > 1)) { |
||||
segments.pop_back(); |
||||
} |
||||
} else if (segment != dot && !segment.empty()) { |
||||
segments.push_back(segment); |
||||
} |
||||
} |
||||
if (p[i] == L'\0') { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
// Handle the case when `path` is just a drive specifier (or some degenerate
|
||||
// form of it, e.g. "c:\..").
|
||||
if (segments.size() == 1 && segments[0].size() == 2 && |
||||
has_drive_letter(segments[0].c_str())) { |
||||
return segments[0] + L'\\'; |
||||
} |
||||
|
||||
// Join all segments.
|
||||
bool first = true; |
||||
std::wstringstream result; |
||||
for (int i = 0; i < segments.size(); ++i) { |
||||
if (!first) { |
||||
result << L'\\'; |
||||
} |
||||
first = false; |
||||
result << segments[i]; |
||||
} |
||||
// Preserve trailing separator if the input contained it.
|
||||
if (!path.empty() && is_separator(p[path.size() - 1])) { |
||||
result << L'\\'; |
||||
} |
||||
return result.str(); |
||||
} |
||||
|
||||
bool as_windows_path(const char* path, wstring* result) { |
||||
if (null_or_empty(path)) { |
||||
result->clear(); |
||||
return true; |
||||
} |
||||
wstring wpath; |
||||
if (!strings::utf8_to_wcs(path, &wpath)) { |
||||
return false; |
||||
} |
||||
if (has_longpath_prefix(wpath.c_str())) { |
||||
*result = wpath; |
||||
return true; |
||||
} |
||||
if (is_separator(path[0]) || is_drive_relative(path)) { |
||||
return false; |
||||
} |
||||
|
||||
|
||||
if (!is_path_absolute(wpath.c_str())) { |
||||
int size = ::GetCurrentDirectoryW(0, nullptr); |
||||
if (size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { |
||||
return false; |
||||
} |
||||
std::unique_ptr<WCHAR[]> wcwd(new WCHAR[size]); |
||||
::GetCurrentDirectoryW(size, wcwd.get()); |
||||
wpath = join_paths(wcwd.get(), wpath); |
||||
} |
||||
wpath = normalize(wpath); |
||||
if (!has_longpath_prefix(wpath.c_str())) { |
||||
// Add the "\\?\" prefix unconditionally. This way we prevent the Win32 API
|
||||
// from processing the path and "helpfully" removing trailing dots from the
|
||||
// path, for example.
|
||||
// See https://github.com/bazelbuild/bazel/issues/2935
|
||||
wpath = wstring(L"\\\\?\\") + wpath; |
||||
} |
||||
*result = wpath; |
||||
return true; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
int open(const char* path, int flags, int mode) { |
||||
#ifdef SUPPORT_LONGPATHS |
||||
wstring wpath; |
||||
if (!as_windows_path(path, &wpath)) { |
||||
errno = ENOENT; |
||||
return -1; |
||||
} |
||||
return ::_wopen(wpath.c_str(), flags, mode); |
||||
#else |
||||
return ::_open(path, flags, mode); |
||||
#endif |
||||
} |
||||
|
||||
int mkdir(const char* path, int _mode) { |
||||
#ifdef SUPPORT_LONGPATHS |
||||
wstring wpath; |
||||
if (!as_windows_path(path, &wpath)) { |
||||
errno = ENOENT; |
||||
return -1; |
||||
} |
||||
return ::_wmkdir(wpath.c_str()); |
||||
#else // not SUPPORT_LONGPATHS
|
||||
return ::_mkdir(path); |
||||
#endif // not SUPPORT_LONGPATHS
|
||||
} |
||||
|
||||
int access(const char* path, int mode) { |
||||
#ifdef SUPPORT_LONGPATHS |
||||
wstring wpath; |
||||
if (!as_windows_path(path, &wpath)) { |
||||
errno = ENOENT; |
||||
return -1; |
||||
} |
||||
return ::_waccess(wpath.c_str(), mode); |
||||
#else |
||||
return ::_access(path, mode); |
||||
#endif |
||||
} |
||||
|
||||
int chdir(const char* path) { |
||||
#ifdef SUPPORT_LONGPATHS |
||||
wstring wpath; |
||||
if (!as_windows_path(path, &wpath)) { |
||||
errno = ENOENT; |
||||
return -1; |
||||
} |
||||
return ::_wchdir(wpath.c_str()); |
||||
#else |
||||
return ::_chdir(path); |
||||
#endif |
||||
} |
||||
|
||||
int stat(const char* path, struct _stat* buffer) { |
||||
#ifdef SUPPORT_LONGPATHS |
||||
wstring wpath; |
||||
if (!as_windows_path(path, &wpath)) { |
||||
errno = ENOENT; |
||||
return -1; |
||||
} |
||||
return ::_wstat(wpath.c_str(), buffer); |
||||
#else // not SUPPORT_LONGPATHS
|
||||
return ::_stat(path, buffer); |
||||
#endif // not SUPPORT_LONGPATHS
|
||||
} |
||||
|
||||
FILE* fopen(const char* path, const char* mode) { |
||||
#ifdef SUPPORT_LONGPATHS |
||||
if (null_or_empty(path)) { |
||||
errno = EINVAL; |
||||
return nullptr; |
||||
} |
||||
wstring wpath; |
||||
if (!as_windows_path(path, &wpath)) { |
||||
errno = ENOENT; |
||||
return nullptr; |
||||
} |
||||
wstring wmode; |
||||
if (!strings::utf8_to_wcs(mode, &wmode)) { |
||||
errno = EINVAL; |
||||
return nullptr; |
||||
} |
||||
return ::_wfopen(wpath.c_str(), wmode.c_str()); |
||||
#else |
||||
return ::fopen(path, mode); |
||||
#endif |
||||
} |
||||
|
||||
int close(int fd) { return ::close(fd); } |
||||
|
||||
int dup(int fd) { return ::_dup(fd); } |
||||
|
||||
int dup2(int fd1, int fd2) { return ::_dup2(fd1, fd2); } |
||||
|
||||
int read(int fd, void* buffer, size_t size) { |
||||
return ::_read(fd, buffer, size); |
||||
} |
||||
|
||||
int setmode(int fd, int mode) { return ::_setmode(fd, mode); } |
||||
|
||||
int write(int fd, const void* buffer, size_t size) { |
||||
return ::_write(fd, buffer, size); |
||||
} |
||||
|
||||
wstring testonly_utf8_to_winpath(const char* path) { |
||||
wstring wpath; |
||||
return as_windows_path(path, &wpath) ? wpath : wstring(); |
||||
} |
||||
|
||||
bool expand_wildcards( |
||||
const string& path, std::function<void(const string&)> consume) { |
||||
if (path.find_first_of("*?") == string::npos) { |
||||
// There are no wildcards in the path, we don't need to expand it.
|
||||
consume(path); |
||||
return ExpandWildcardsResult::kSuccess; |
||||
} |
||||
|
||||
#ifdef SUPPORT_LONGPATHS |
||||
|
||||
wstring wpath; |
||||
if (!as_windows_path(path.c_str(), &wpath)) { |
||||
return ExpandWildcardsResult::kErrorInputPathConversion; |
||||
} |
||||
|
||||
static const wstring kDot = L"."; |
||||
static const wstring kDotDot = L".."; |
||||
WIN32_FIND_DATAW metadata; |
||||
HANDLE handle = ::FindFirstFileW(wpath.c_str(), &metadata); |
||||
if (handle == INVALID_HANDLE_VALUE) { |
||||
// The pattern does not match any files (or directories).
|
||||
return ExpandWildcardsResult::kErrorNoMatchingFile; |
||||
} |
||||
|
||||
string::size_type pos = path.find_last_of("\\/"); |
||||
string dirname; |
||||
if (pos != string::npos) { |
||||
dirname = path.substr(0, pos + 1); |
||||
} |
||||
|
||||
int matched = ExpandWildcardsResult::kErrorNoMatchingFile; |
||||
do { |
||||
// Ignore ".", "..", and directories.
|
||||
if ((metadata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 |
||||
&& kDot != metadata.cFileName && kDotDot != metadata.cFileName) { |
||||
matched = ExpandWildcardsResult::kSuccess; |
||||
string filename; |
||||
if (!strings::wcs_to_utf8(metadata.cFileName, &filename)) { |
||||
return ExpandWildcardsResult::kErrorOutputPathConversion; |
||||
} |
||||
|
||||
if (dirname.empty()) { |
||||
consume(filename); |
||||
} else { |
||||
consume(dirname + filename); |
||||
} |
||||
} |
||||
} while (::FindNextFileW(handle, &metadata)); |
||||
FindClose(handle); |
||||
return matched; |
||||
|
||||
#else // not SUPPORT_LONGPATHS
|
||||
|
||||
static const string kDot = "."; |
||||
static const string kDotDot = ".."; |
||||
WIN32_FIND_DATAA metadata; |
||||
HANDLE handle = ::FindFirstFileA(path.c_str(), &metadata); |
||||
if (handle == INVALID_HANDLE_VALUE) { |
||||
// The pattern does not match any files (or directories).
|
||||
return ExpandWildcardsResult::kErrorNoMatchingFile; |
||||
} |
||||
|
||||
string::size_type pos = path.find_last_of("\\/"); |
||||
string dirname; |
||||
if (pos != string::npos) { |
||||
dirname = path.substr(0, pos + 1); |
||||
} |
||||
|
||||
int matched = ExpandWildcardsResult::kErrorNoMatchingFile; |
||||
do { |
||||
// Ignore ".", "..", and directories.
|
||||
if ((metadata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 |
||||
&& kDot != metadata.cFileName && kDotDot != metadata.cFileName) { |
||||
matched = ExpandWildcardsResult::kSuccess; |
||||
if (!dirname.empty()) { |
||||
consume(dirname + metadata.cFileName); |
||||
} else { |
||||
consume(metadata.cFileName); |
||||
} |
||||
} |
||||
} while (::FindNextFileA(handle, &metadata)); |
||||
FindClose(handle); |
||||
return matched; |
||||
#endif // SUPPORT_LONGPATHS
|
||||
} |
||||
|
||||
namespace strings { |
||||
|
||||
bool wcs_to_mbs(const WCHAR* s, string* out, bool outUtf8) { |
||||
if (null_or_empty(s)) { |
||||
out->clear(); |
||||
return true; |
||||
} |
||||
BOOL usedDefaultChar = FALSE; |
||||
SetLastError(0); |
||||
int size = WideCharToMultiByte( |
||||
outUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, nullptr, 0, nullptr, |
||||
outUtf8 ? nullptr : &usedDefaultChar); |
||||
if ((size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) |
||||
|| usedDefaultChar) { |
||||
return false; |
||||
} |
||||
std::unique_ptr<CHAR[]> astr(new CHAR[size]); |
||||
WideCharToMultiByte( |
||||
outUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, astr.get(), size, nullptr, nullptr); |
||||
out->assign(astr.get()); |
||||
return true; |
||||
} |
||||
|
||||
bool mbs_to_wcs(const char* s, wstring* out, bool inUtf8) { |
||||
if (null_or_empty(s)) { |
||||
out->clear(); |
||||
return true; |
||||
} |
||||
|
||||
SetLastError(0); |
||||
int size = |
||||
MultiByteToWideChar(inUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, nullptr, 0); |
||||
if (size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { |
||||
return false; |
||||
} |
||||
std::unique_ptr<WCHAR[]> wstr(new WCHAR[size]); |
||||
MultiByteToWideChar( |
||||
inUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, wstr.get(), size + 1); |
||||
out->assign(wstr.get()); |
||||
return true; |
||||
} |
||||
|
||||
bool utf8_to_wcs(const char* input, wstring* out) { |
||||
return mbs_to_wcs(input, out, true); |
||||
} |
||||
|
||||
bool wcs_to_utf8(const wchar_t* input, string* out) { |
||||
return wcs_to_mbs(input, out, true); |
||||
} |
||||
|
||||
} // namespace strings
|
||||
} // namespace win32
|
||||
} // namespace internal
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#endif // defined(_WIN32)
|
@ -1,140 +0,0 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: laszlocsomor@google.com (Laszlo Csomor)
|
||||
//
|
||||
// This file contains the declarations for Windows implementations of
|
||||
// commonly used POSIX functions such as open(2) and access(2), as well
|
||||
// as macro definitions for flags of these functions.
|
||||
//
|
||||
// By including this file you'll redefine open/access/etc. to
|
||||
// ::google::protobuf::internal::win32::{open/access/etc.}.
|
||||
// Make sure you don't include a header that attempts to redeclare or
|
||||
// redefine these functions, that'll lead to confusing compilation
|
||||
// errors. It's best to #include this file as the last one to ensure that.
|
||||
//
|
||||
// This file is only used on Windows, it's empty on other platforms.
|
||||
|
||||
#ifndef GOOGLE_PROTOBUF_STUBS_IO_WIN32_H__ |
||||
#define GOOGLE_PROTOBUF_STUBS_IO_WIN32_H__ |
||||
|
||||
#if defined(_WIN32) |
||||
|
||||
#include <functional> |
||||
#include <string> |
||||
#include <google/protobuf/stubs/port.h> |
||||
|
||||
// Compilers on Windows other than MSVC (e.g. Cygwin, MinGW32) define the
|
||||
// following functions already, except for mkdir.
|
||||
namespace google { |
||||
namespace protobuf { |
||||
namespace internal { |
||||
namespace win32 { |
||||
|
||||
LIBPROTOBUF_EXPORT FILE* fopen(const char* path, const char* mode); |
||||
LIBPROTOBUF_EXPORT int access(const char* path, int mode); |
||||
LIBPROTOBUF_EXPORT int chdir(const char* path); |
||||
LIBPROTOBUF_EXPORT int close(int fd); |
||||
LIBPROTOBUF_EXPORT int dup(int fd); |
||||
LIBPROTOBUF_EXPORT int dup2(int fd1, int fd2); |
||||
LIBPROTOBUF_EXPORT int mkdir(const char* path, int _mode); |
||||
LIBPROTOBUF_EXPORT int open(const char* path, int flags, int mode = 0); |
||||
LIBPROTOBUF_EXPORT int read(int fd, void* buffer, size_t size); |
||||
LIBPROTOBUF_EXPORT int setmode(int fd, int mode); |
||||
LIBPROTOBUF_EXPORT int stat(const char* path, struct _stat* buffer); |
||||
LIBPROTOBUF_EXPORT int write(int fd, const void* buffer, size_t size); |
||||
LIBPROTOBUF_EXPORT std::wstring testonly_utf8_to_winpath(const char* path); |
||||
|
||||
struct ExpandWildcardsResult { |
||||
enum { |
||||
kSuccess = 0, |
||||
kErrorNoMatchingFile = 1, |
||||
kErrorInputPathConversion = 2, |
||||
kErrorResultPathConversion = 3, |
||||
}; |
||||
}; |
||||
|
||||
// Expand wildcards in a path pattern, feed the result to a consumer function.
|
||||
//
|
||||
// `path` must be a valid, Windows-style path. It may be absolute, or relative
|
||||
// to the current working directory, and it may contain wildcards ("*" and "?")
|
||||
// in the last path segment. This function passes all matching file names to
|
||||
// `consume`. The resulting paths may not be absolute nor normalized.
|
||||
//
|
||||
// The function returns true if the path did not contain any wildcards (in
|
||||
// which case the path may or may not exist), or the path did contain wildcards
|
||||
// and it matched at least one file.
|
||||
// The function returns false if the path contained wildcards but it did not
|
||||
// match any files.
|
||||
LIBPROTOBUF_EXPORT bool expand_wildcards( |
||||
const std::string& path, std::function<void(const std::string&)> consume); |
||||
|
||||
namespace strings { |
||||
|
||||
// Convert from UTF-16 to Active-Code-Page-encoded or to UTF-8-encoded text.
|
||||
LIBPROTOBUF_EXPORT bool wcs_to_mbs( |
||||
const wchar_t* s, std::string* out, bool outUtf8); |
||||
|
||||
// Convert from Active-Code-Page-encoded or UTF-8-encoded text to UTF-16.
|
||||
LIBPROTOBUF_EXPORT bool mbs_to_wcs( |
||||
const char* s, std::wstring* out, bool inUtf8); |
||||
|
||||
// Convert from UTF-8-encoded text to UTF-16.
|
||||
LIBPROTOBUF_EXPORT bool utf8_to_wcs(const char* input, std::wstring* out); |
||||
|
||||
// Convert from UTF-16-encoded text to UTF-8.
|
||||
LIBPROTOBUF_EXPORT bool wcs_to_utf8(const wchar_t* input, std::string* out); |
||||
|
||||
} // namespace strings
|
||||
|
||||
} // namespace win32
|
||||
} // namespace internal
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#ifndef W_OK |
||||
#define W_OK 02 // not defined by MSVC for whatever reason
|
||||
#endif |
||||
|
||||
#ifndef F_OK |
||||
#define F_OK 00 // not defined by MSVC for whatever reason
|
||||
#endif |
||||
|
||||
#ifndef STDIN_FILENO |
||||
#define STDIN_FILENO 0 |
||||
#endif |
||||
|
||||
#ifndef STDOUT_FILENO |
||||
#define STDOUT_FILENO 1 |
||||
#endif |
||||
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#endif // GOOGLE_PROTOBUF_STUBS_IO_WIN32_H__
|
@ -1,639 +0,0 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: laszlocsomor@google.com (Laszlo Csomor)
|
||||
//
|
||||
// Unit tests for long-path-aware open/mkdir/access/etc. on Windows, as well as
|
||||
// for the supporting utility functions.
|
||||
//
|
||||
// This file is only used on Windows, it's empty on other platforms.
|
||||
|
||||
#if defined(_WIN32) |
||||
|
||||
#define WIN32_LEAN_AND_MEAN |
||||
#include <errno.h> |
||||
#include <fcntl.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <sys/stat.h> |
||||
#include <sys/types.h> |
||||
#include <wchar.h> |
||||
#include <windows.h> |
||||
|
||||
#include <google/protobuf/stubs/io_win32.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <memory> |
||||
#include <sstream> |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
namespace internal { |
||||
namespace win32 { |
||||
namespace { |
||||
|
||||
const char kUtf8Text[] = { |
||||
'h', 'i', ' ', |
||||
// utf-8: 11010000 10011111, utf-16: 100 0001 1111 = 0x041F
|
||||
static_cast<char>(0xd0), static_cast<char>(0x9f), |
||||
// utf-8: 11010001 10000000, utf-16: 100 0100 0000 = 0x0440
|
||||
static_cast<char>(0xd1), static_cast<char>(0x80), |
||||
// utf-8: 11010000 10111000, utf-16: 100 0011 1000 = 0x0438
|
||||
static_cast<char>(0xd0), static_cast<char>(0xb8), |
||||
// utf-8: 11010000 10110010, utf-16: 100 0011 0010 = 0x0432
|
||||
static_cast<char>(0xd0), static_cast<char>(0xb2), |
||||
// utf-8: 11010000 10110101, utf-16: 100 0011 0101 = 0x0435
|
||||
static_cast<char>(0xd0), static_cast<char>(0xb5), |
||||
// utf-8: 11010001 10000010, utf-16: 100 0100 0010 = 0x0442
|
||||
static_cast<char>(0xd1), static_cast<char>(0x82), 0 |
||||
}; |
||||
|
||||
const wchar_t kUtf16Text[] = { |
||||
L'h', L'i', L' ', |
||||
L'\x41f', L'\x440', L'\x438', L'\x432', L'\x435', L'\x442', 0 |
||||
}; |
||||
|
||||
using std::string; |
||||
using std::vector; |
||||
using std::wstring; |
||||
|
||||
class IoWin32Test : public ::testing::Test { |
||||
public: |
||||
void SetUp(); |
||||
void TearDown(); |
||||
|
||||
protected: |
||||
bool CreateAllUnder(wstring path); |
||||
bool DeleteAllUnder(wstring path); |
||||
|
||||
WCHAR working_directory[MAX_PATH]; |
||||
string test_tmpdir; |
||||
wstring wtest_tmpdir; |
||||
}; |
||||
|
||||
#define ASSERT_INITIALIZED \ |
||||
{ \
|
||||
EXPECT_FALSE(test_tmpdir.empty()); \
|
||||
EXPECT_FALSE(wtest_tmpdir.empty()); \
|
||||
} |
||||
|
||||
namespace { |
||||
void StripTrailingSlashes(string* str) { |
||||
int i = str->size() - 1; |
||||
for (; i >= 0 && ((*str)[i] == '/' || (*str)[i] == '\\'); --i) {} |
||||
str->resize(i+1); |
||||
} |
||||
|
||||
bool GetEnvVarAsUtf8(const WCHAR* name, string* result) { |
||||
DWORD size = ::GetEnvironmentVariableW(name, nullptr, 0); |
||||
if (size > 0 && GetLastError() != ERROR_ENVVAR_NOT_FOUND) { |
||||
std::unique_ptr<WCHAR[]> wcs(new WCHAR[size]); |
||||
::GetEnvironmentVariableW(name, wcs.get(), size); |
||||
// GetEnvironmentVariableA retrieves an Active-Code-Page-encoded text which
|
||||
// we'd first need to convert to UTF-16 then to UTF-8, because there seems
|
||||
// to be no API function to do that conversion directly.
|
||||
// GetEnvironmentVariableW retrieves an UTF-16-encoded text, which we need
|
||||
// to convert to UTF-8.
|
||||
return strings::wcs_to_utf8(wcs.get(), result); |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
bool GetCwdAsUtf8(string* result) { |
||||
DWORD size = ::GetCurrentDirectoryW(0, nullptr); |
||||
if (size > 0) { |
||||
std::unique_ptr<WCHAR[]> wcs(new WCHAR[size]); |
||||
::GetCurrentDirectoryW(size, wcs.get()); |
||||
// GetCurrentDirectoryA retrieves an Active-Code-Page-encoded text which
|
||||
// we'd first need to convert to UTF-16 then to UTF-8, because there seems
|
||||
// to be no API function to do that conversion directly.
|
||||
// GetCurrentDirectoryW retrieves an UTF-16-encoded text, which we need
|
||||
// to convert to UTF-8.
|
||||
return strings::wcs_to_utf8(wcs.get(), result); |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
bool CreateEmptyFile(const wstring& path) { |
||||
HANDLE h = CreateFileW(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, |
||||
FILE_ATTRIBUTE_NORMAL, NULL); |
||||
if (h == INVALID_HANDLE_VALUE) { |
||||
return false; |
||||
} |
||||
CloseHandle(h); |
||||
return true; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
void IoWin32Test::SetUp() { |
||||
test_tmpdir.clear(); |
||||
wtest_tmpdir.clear(); |
||||
DWORD size = ::GetCurrentDirectoryW(MAX_PATH, working_directory); |
||||
EXPECT_GT(size, 0); |
||||
EXPECT_LT(size, MAX_PATH); |
||||
|
||||
string tmp; |
||||
bool ok = false; |
||||
if (!ok) { |
||||
// Bazel sets this environment variable when it runs tests.
|
||||
ok = GetEnvVarAsUtf8(L"TEST_TMPDIR", &tmp); |
||||
} |
||||
if (!ok) { |
||||
// Bazel 0.8.0 sets this environment for every build and test action.
|
||||
ok = GetEnvVarAsUtf8(L"TEMP", &tmp); |
||||
} |
||||
if (!ok) { |
||||
// Bazel 0.8.0 sets this environment for every build and test action.
|
||||
ok = GetEnvVarAsUtf8(L"TMP", &tmp); |
||||
} |
||||
if (!ok) { |
||||
// Fall back to using the current directory.
|
||||
ok = GetCwdAsUtf8(&tmp); |
||||
} |
||||
if (!ok || tmp.empty()) { |
||||
FAIL() << "Cannot find a temp directory."; |
||||
} |
||||
|
||||
StripTrailingSlashes(&tmp); |
||||
std::stringstream result; |
||||
// Deleting files and directories is asynchronous on Windows, and if TearDown
|
||||
// just deleted the previous temp directory, sometimes we cannot recreate the
|
||||
// same directory.
|
||||
// Use a counter so every test method gets its own temp directory.
|
||||
static unsigned int counter = 0; |
||||
result << tmp << "\\w32tst" << counter++ << ".tmp"; |
||||
test_tmpdir = result.str(); |
||||
wtest_tmpdir = testonly_utf8_to_winpath(test_tmpdir.c_str()); |
||||
ASSERT_FALSE(wtest_tmpdir.empty()); |
||||
ASSERT_TRUE(DeleteAllUnder(wtest_tmpdir)); |
||||
ASSERT_TRUE(CreateAllUnder(wtest_tmpdir)); |
||||
} |
||||
|
||||
void IoWin32Test::TearDown() { |
||||
if (!wtest_tmpdir.empty()) { |
||||
DeleteAllUnder(wtest_tmpdir); |
||||
} |
||||
::SetCurrentDirectoryW(working_directory); |
||||
} |
||||
|
||||
bool IoWin32Test::CreateAllUnder(wstring path) { |
||||
// Prepend UNC prefix if the path doesn't have it already. Don't bother
|
||||
// checking if the path is shorter than MAX_PATH, let's just do it
|
||||
// unconditionally.
|
||||
if (path.find(L"\\\\?\\") != 0) { |
||||
path = wstring(L"\\\\?\\") + path; |
||||
} |
||||
if (::CreateDirectoryW(path.c_str(), nullptr) || |
||||
GetLastError() == ERROR_ALREADY_EXISTS || |
||||
GetLastError() == ERROR_ACCESS_DENIED) { |
||||
return true; |
||||
} |
||||
if (GetLastError() == ERROR_PATH_NOT_FOUND) { |
||||
size_t pos = path.find_last_of(L'\\'); |
||||
if (pos != wstring::npos) { |
||||
wstring parent(path, 0, pos); |
||||
if (CreateAllUnder(parent) && CreateDirectoryW(path.c_str(), nullptr)) { |
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
bool IoWin32Test::DeleteAllUnder(wstring path) { |
||||
static const wstring kDot(L"."); |
||||
static const wstring kDotDot(L".."); |
||||
|
||||
// Prepend UNC prefix if the path doesn't have it already. Don't bother
|
||||
// checking if the path is shorter than MAX_PATH, let's just do it
|
||||
// unconditionally.
|
||||
if (path.find(L"\\\\?\\") != 0) { |
||||
path = wstring(L"\\\\?\\") + path; |
||||
} |
||||
// Append "\" if necessary.
|
||||
if (path[path.size() - 1] != L'\\') { |
||||
path.push_back(L'\\'); |
||||
} |
||||
|
||||
WIN32_FIND_DATAW metadata; |
||||
HANDLE handle = ::FindFirstFileW((path + L"*").c_str(), &metadata); |
||||
if (handle == INVALID_HANDLE_VALUE) { |
||||
return true; // directory doesn't exist
|
||||
} |
||||
|
||||
bool result = true; |
||||
do { |
||||
wstring childname = metadata.cFileName; |
||||
if (kDot != childname && kDotDot != childname) { |
||||
wstring childpath = path + childname; |
||||
if ((metadata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { |
||||
// If this is not a junction, delete its contents recursively.
|
||||
// Finally delete this directory/junction too.
|
||||
if (((metadata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0 && |
||||
!DeleteAllUnder(childpath)) || |
||||
!::RemoveDirectoryW(childpath.c_str())) { |
||||
result = false; |
||||
break; |
||||
} |
||||
} else { |
||||
if (!::DeleteFileW(childpath.c_str())) { |
||||
result = false; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} while (::FindNextFileW(handle, &metadata)); |
||||
::FindClose(handle); |
||||
return result; |
||||
} |
||||
|
||||
TEST_F(IoWin32Test, AccessTest) { |
||||
ASSERT_INITIALIZED; |
||||
|
||||
string path = test_tmpdir; |
||||
while (path.size() < MAX_PATH - 30) { |
||||
path += "\\accesstest"; |
||||
EXPECT_EQ(mkdir(path.c_str(), 0644), 0); |
||||
} |
||||
string file = path + "\\file.txt"; |
||||
int fd = open(file.c_str(), O_CREAT | O_WRONLY, 0644); |
||||
if (fd > 0) { |
||||
EXPECT_EQ(close(fd), 0); |
||||
} else { |
||||
EXPECT_TRUE(false); |
||||
} |
||||
|
||||
EXPECT_EQ(access(test_tmpdir.c_str(), F_OK), 0); |
||||
EXPECT_EQ(access(path.c_str(), F_OK), 0); |
||||
EXPECT_EQ(access(path.c_str(), W_OK), 0); |
||||
EXPECT_EQ(access(file.c_str(), F_OK | W_OK), 0); |
||||
EXPECT_NE(access((file + ".blah").c_str(), F_OK), 0); |
||||
EXPECT_NE(access((file + ".blah").c_str(), W_OK), 0); |
||||
|
||||
EXPECT_EQ(access(".", F_OK), 0); |
||||
EXPECT_EQ(access(".", W_OK), 0); |
||||
EXPECT_EQ(access((test_tmpdir + "/accesstest").c_str(), F_OK | W_OK), 0); |
||||
ASSERT_EQ(access((test_tmpdir + "/./normalize_me/.././accesstest").c_str(), |
||||
F_OK | W_OK), |
||||
0); |
||||
EXPECT_NE(access("io_win32_unittest.AccessTest.nonexistent", F_OK), 0); |
||||
EXPECT_NE(access("io_win32_unittest.AccessTest.nonexistent", W_OK), 0); |
||||
|
||||
ASSERT_EQ(access("c:bad", F_OK), -1); |
||||
ASSERT_EQ(errno, ENOENT); |
||||
ASSERT_EQ(access("/tmp/bad", F_OK), -1); |
||||
ASSERT_EQ(errno, ENOENT); |
||||
ASSERT_EQ(access("\\bad", F_OK), -1); |
||||
ASSERT_EQ(errno, ENOENT); |
||||
} |
||||
|
||||
TEST_F(IoWin32Test, OpenTest) { |
||||
ASSERT_INITIALIZED; |
||||
|
||||
string path = test_tmpdir; |
||||
while (path.size() < MAX_PATH) { |
||||
path += "\\opentest"; |
||||
EXPECT_EQ(mkdir(path.c_str(), 0644), 0); |
||||
} |
||||
string file = path + "\\file.txt"; |
||||
int fd = open(file.c_str(), O_CREAT | O_WRONLY, 0644); |
||||
if (fd > 0) { |
||||
EXPECT_EQ(write(fd, "hello", 5), 5); |
||||
EXPECT_EQ(close(fd), 0); |
||||
} else { |
||||
EXPECT_TRUE(false); |
||||
} |
||||
|
||||
ASSERT_EQ(open("c:bad.txt", O_CREAT | O_WRONLY, 0644), -1); |
||||
ASSERT_EQ(errno, ENOENT); |
||||
ASSERT_EQ(open("/tmp/bad.txt", O_CREAT | O_WRONLY, 0644), -1); |
||||
ASSERT_EQ(errno, ENOENT); |
||||
ASSERT_EQ(open("\\bad.txt", O_CREAT | O_WRONLY, 0644), -1); |
||||
ASSERT_EQ(errno, ENOENT); |
||||
} |
||||
|
||||
TEST_F(IoWin32Test, MkdirTest) { |
||||
ASSERT_INITIALIZED; |
||||
|
||||
string path = test_tmpdir; |
||||
do { |
||||
path += "\\mkdirtest"; |
||||
ASSERT_EQ(mkdir(path.c_str(), 0644), 0); |
||||
} while (path.size() <= MAX_PATH); |
||||
|
||||
ASSERT_EQ(mkdir("c:bad", 0644), -1); |
||||
ASSERT_EQ(errno, ENOENT); |
||||
ASSERT_EQ(mkdir("/tmp/bad", 0644), -1); |
||||
ASSERT_EQ(errno, ENOENT); |
||||
ASSERT_EQ(mkdir("\\bad", 0644), -1); |
||||
ASSERT_EQ(errno, ENOENT); |
||||
} |
||||
|
||||
TEST_F(IoWin32Test, MkdirTestNonAscii) { |
||||
ASSERT_INITIALIZED; |
||||
|
||||
// Create a non-ASCII path.
|
||||
// Ensure that we can create the directory using CreateDirectoryW.
|
||||
EXPECT_TRUE(CreateDirectoryW((wtest_tmpdir + L"\\1").c_str(), nullptr)); |
||||
EXPECT_TRUE(CreateDirectoryW((wtest_tmpdir + L"\\1\\" + kUtf16Text).c_str(), nullptr)); |
||||
// Ensure that we can create a very similarly named directory using mkdir.
|
||||
// We don't attemp to delete and recreate the same directory, because on
|
||||
// Windows, deleting files and directories seems to be asynchronous.
|
||||
EXPECT_EQ(mkdir((test_tmpdir + "\\2").c_str(), 0644), 0); |
||||
EXPECT_EQ(mkdir((test_tmpdir + "\\2\\" + kUtf8Text).c_str(), 0644), 0); |
||||
} |
||||
|
||||
TEST_F(IoWin32Test, ChdirTest) { |
||||
string path("C:\\"); |
||||
EXPECT_EQ(access(path.c_str(), F_OK), 0); |
||||
ASSERT_EQ(chdir(path.c_str()), 0); |
||||
|
||||
// Do not try to chdir into the test_tmpdir, it may already contain directory
|
||||
// names with trailing dots.
|
||||
// Instead test here with an obviously dot-trailed path. If the win32_chdir
|
||||
// function would not convert the path to absolute and prefix with "\\?\" then
|
||||
// the Win32 API would ignore the trailing dot, but because of the prefixing
|
||||
// there'll be no path processing done, so we'll actually attempt to chdir
|
||||
// into "C:\some\path\foo."
|
||||
path = test_tmpdir + "/foo."; |
||||
EXPECT_EQ(mkdir(path.c_str(), 644), 0); |
||||
EXPECT_EQ(access(path.c_str(), F_OK), 0); |
||||
ASSERT_NE(chdir(path.c_str()), 0); |
||||
} |
||||
|
||||
TEST_F(IoWin32Test, ChdirTestNonAscii) { |
||||
ASSERT_INITIALIZED; |
||||
|
||||
// Create a directory with a non-ASCII path and ensure we can cd into it.
|
||||
wstring wNonAscii(wtest_tmpdir + L"\\" + kUtf16Text); |
||||
string nonAscii; |
||||
EXPECT_TRUE(strings::wcs_to_utf8(wNonAscii.c_str(), &nonAscii)); |
||||
EXPECT_TRUE(CreateDirectoryW(wNonAscii.c_str(), nullptr)); |
||||
WCHAR cwd[MAX_PATH]; |
||||
EXPECT_TRUE(GetCurrentDirectoryW(MAX_PATH, cwd)); |
||||
// Ensure that we can cd into the path using SetCurrentDirectoryW.
|
||||
EXPECT_TRUE(SetCurrentDirectoryW(wNonAscii.c_str())); |
||||
EXPECT_TRUE(SetCurrentDirectoryW(cwd)); |
||||
// Ensure that we can cd into the path using chdir.
|
||||
ASSERT_EQ(chdir(nonAscii.c_str()), 0); |
||||
// Ensure that the GetCurrentDirectoryW returns the desired path.
|
||||
EXPECT_TRUE(GetCurrentDirectoryW(MAX_PATH, cwd)); |
||||
ASSERT_EQ(wNonAscii, cwd); |
||||
} |
||||
|
||||
TEST_F(IoWin32Test, ExpandWildcardsInRelativePathTest) { |
||||
wstring wNonAscii(wtest_tmpdir + L"\\" + kUtf16Text); |
||||
EXPECT_TRUE(CreateDirectoryW(wNonAscii.c_str(), nullptr)); |
||||
// Create mock files we will test pattern matching on.
|
||||
EXPECT_TRUE(CreateEmptyFile(wNonAscii + L"\\foo_a.proto")); |
||||
EXPECT_TRUE(CreateEmptyFile(wNonAscii + L"\\foo_b.proto")); |
||||
EXPECT_TRUE(CreateEmptyFile(wNonAscii + L"\\bar.proto")); |
||||
// `cd` into `wtest_tmpdir`.
|
||||
EXPECT_TRUE(SetCurrentDirectoryW(wtest_tmpdir.c_str())); |
||||
|
||||
int found_a = 0; |
||||
int found_b = 0; |
||||
vector<string> found_bad; |
||||
// Assert matching a relative path pattern. Results should also be relative.
|
||||
int result =
|
||||
expand_wildcards( |
||||
string(kUtf8Text) + "\\foo*.proto", |
||||
[&found_a, &found_b, &found_bad](const string& p) { |
||||
if (p == string(kUtf8Text) + "\\foo_a.proto") { |
||||
found_a++; |
||||
} else if (p == string(kUtf8Text) + "\\foo_b.proto") { |
||||
found_b++; |
||||
} else { |
||||
found_bad.push_back(p); |
||||
} |
||||
}); |
||||
EXPECT_EQ(result, ExpandWildcardsResult::kSuccess); |
||||
EXPECT_EQ(found_a, 1); |
||||
EXPECT_EQ(found_b, 1); |
||||
if (!found_bad.empty()) { |
||||
FAIL() << found_bad[0]; |
||||
} |
||||
|
||||
// Assert matching the exact filename.
|
||||
found_a = 0; |
||||
found_bad.clear(); |
||||
result =
|
||||
expand_wildcards( |
||||
string(kUtf8Text) + "\\foo_a.proto", |
||||
[&found_a, &found_bad](const string& p) { |
||||
if (p == string(kUtf8Text) + "\\foo_a.proto") { |
||||
found_a++; |
||||
} else { |
||||
found_bad.push_back(p); |
||||
} |
||||
}); |
||||
EXPECT_EQ(result, ExpandWildcardsResult::kSuccess); |
||||
EXPECT_EQ(found_a, 1); |
||||
if (!found_bad.empty()) { |
||||
FAIL() << found_bad[0]; |
||||
} |
||||
} |
||||
|
||||
TEST_F(IoWin32Test, ExpandWildcardsInAbsolutePathTest) { |
||||
wstring wNonAscii(wtest_tmpdir + L"\\" + kUtf16Text); |
||||
EXPECT_TRUE(CreateDirectoryW(wNonAscii.c_str(), nullptr)); |
||||
// Create mock files we will test pattern matching on.
|
||||
EXPECT_TRUE(CreateEmptyFile(wNonAscii + L"\\foo_a.proto")); |
||||
EXPECT_TRUE(CreateEmptyFile(wNonAscii + L"\\foo_b.proto")); |
||||
EXPECT_TRUE(CreateEmptyFile(wNonAscii + L"\\bar.proto")); |
||||
|
||||
int found_a = 0; |
||||
int found_b = 0; |
||||
vector<string> found_bad; |
||||
// Assert matching an absolute path. The results should also use absolute
|
||||
// path.
|
||||
int result =
|
||||
expand_wildcards( |
||||
string(test_tmpdir) + "\\" + kUtf8Text + "\\foo*.proto", |
||||
[this, &found_a, &found_b, &found_bad](const string& p) { |
||||
if (p == string(this->test_tmpdir) |
||||
+ "\\" |
||||
+ kUtf8Text |
||||
+ "\\foo_a.proto") { |
||||
found_a++; |
||||
} else if (p == string(this->test_tmpdir) |
||||
+ "\\" |
||||
+ kUtf8Text |
||||
+ "\\foo_b.proto") { |
||||
found_b++; |
||||
} else { |
||||
found_bad.push_back(p); |
||||
} |
||||
}); |
||||
EXPECT_EQ(result, ExpandWildcardsResult::kSuccess); |
||||
EXPECT_EQ(found_a, 1); |
||||
EXPECT_EQ(found_b, 1); |
||||
if (!found_bad.empty()) { |
||||
FAIL() << found_bad[0]; |
||||
} |
||||
|
||||
// Assert matching the exact filename.
|
||||
found_a = 0; |
||||
found_bad.clear(); |
||||
result =
|
||||
expand_wildcards( |
||||
string(test_tmpdir) + "\\" + kUtf8Text + "\\foo_a.proto", |
||||
[this, &found_a, &found_bad](const string& p) { |
||||
if (p == string(this->test_tmpdir) |
||||
+ "\\" |
||||
+ kUtf8Text |
||||
+ "\\foo_a.proto") { |
||||
found_a++; |
||||
} else { |
||||
found_bad.push_back(p); |
||||
} |
||||
}); |
||||
EXPECT_EQ(result, ExpandWildcardsResult::kSuccess); |
||||
EXPECT_EQ(found_a, 1); |
||||
if (!found_bad.empty()) { |
||||
FAIL() << found_bad[0]; |
||||
} |
||||
} |
||||
|
||||
TEST_F(IoWin32Test, ExpandWildcardsIgnoresDirectoriesTest) { |
||||
wstring wNonAscii(wtest_tmpdir + L"\\" + kUtf16Text); |
||||
EXPECT_TRUE(CreateDirectoryW(wNonAscii.c_str(), nullptr)); |
||||
// Create mock files we will test pattern matching on.
|
||||
EXPECT_TRUE(CreateEmptyFile(wNonAscii + L"\\foo_a.proto")); |
||||
EXPECT_TRUE(CreateDirectoryW((wNonAscii + L"\\foo_b.proto").c_str(), nullptr)); |
||||
EXPECT_TRUE(CreateEmptyFile(wNonAscii + L"\\foo_c.proto")); |
||||
// `cd` into `wtest_tmpdir`.
|
||||
EXPECT_TRUE(SetCurrentDirectoryW(wtest_tmpdir.c_str())); |
||||
|
||||
int found_a = 0; |
||||
int found_c = 0; |
||||
vector<string> found_bad; |
||||
// Assert that the pattern matches exactly the expected files, and using the
|
||||
// absolute path as did the input pattern.
|
||||
int result =
|
||||
expand_wildcards( |
||||
string(kUtf8Text) + "\\foo*.proto", |
||||
[&found_a, &found_c, &found_bad](const string& p) { |
||||
if (p == string(kUtf8Text) + "\\foo_a.proto") { |
||||
found_a++; |
||||
} else if (p == string(kUtf8Text) + "\\foo_c.proto") { |
||||
found_c++; |
||||
} else { |
||||
found_bad.push_back(p); |
||||
} |
||||
}); |
||||
EXPECT_EQ(result, ExpandWildcardsResult::kSuccess); |
||||
EXPECT_EQ(found_a, 1); |
||||
EXPECT_EQ(found_c, 1); |
||||
if (!found_bad.empty()) { |
||||
FAIL() << found_bad[0]; |
||||
} |
||||
} |
||||
|
||||
TEST_F(IoWin32Test, ExpandWildcardsFailsIfNoFileMatchesTest) { |
||||
wstring wNonAscii(wtest_tmpdir + L"\\" + kUtf16Text); |
||||
EXPECT_TRUE(CreateDirectoryW(wNonAscii.c_str(), nullptr)); |
||||
// Create mock files we will test pattern matching on.
|
||||
EXPECT_TRUE(CreateEmptyFile(wNonAscii + L"\\foo_a.proto")); |
||||
// `cd` into `wtest_tmpdir`.
|
||||
EXPECT_TRUE(SetCurrentDirectoryW(wtest_tmpdir.c_str())); |
||||
|
||||
// Control test: should match foo*.proto
|
||||
int result = expand_wildcards( |
||||
string(kUtf8Text) + "\\foo*.proto", [](const string&) {}); |
||||
EXPECT_EQ(result, ExpandWildcardsResult::kSuccess); |
||||
|
||||
// Control test: should match foo_a.proto
|
||||
result = expand_wildcards( |
||||
string(kUtf8Text) + "\\foo_a.proto", [](const string&) {}); |
||||
EXPECT_EQ(result, ExpandWildcardsResult::kSuccess); |
||||
|
||||
// Actual test: should not match anything.
|
||||
int result = expand_wildcards( |
||||
string(kUtf8Text) + "\\bar*.proto", [](const string&) {}); |
||||
ASSERT_EQ(result, ExpandWildcardsResult::kErrorNoMatchingFile); |
||||
} |
||||
|
||||
TEST_F(IoWin32Test, AsWindowsPathTest) { |
||||
DWORD size = GetCurrentDirectoryW(0, nullptr); |
||||
std::unique_ptr<wchar_t[]> cwd_str(new wchar_t[size]); |
||||
EXPECT_GT(GetCurrentDirectoryW(size, cwd_str.get()), 0); |
||||
wstring cwd = wstring(L"\\\\?\\") + cwd_str.get(); |
||||
|
||||
ASSERT_EQ(testonly_utf8_to_winpath("relative_mkdirtest"), |
||||
cwd + L"\\relative_mkdirtest"); |
||||
ASSERT_EQ(testonly_utf8_to_winpath("preserve//\\trailing///"), |
||||
cwd + L"\\preserve\\trailing\\"); |
||||
ASSERT_EQ(testonly_utf8_to_winpath("./normalize_me\\/../blah"), |
||||
cwd + L"\\blah"); |
||||
std::ostringstream relpath; |
||||
for (wchar_t* p = cwd_str.get(); *p; ++p) { |
||||
if (*p == '/' || *p == '\\') { |
||||
relpath << "../"; |
||||
} |
||||
} |
||||
relpath << ".\\/../\\./beyond-toplevel"; |
||||
ASSERT_EQ(testonly_utf8_to_winpath(relpath.str().c_str()), |
||||
wstring(L"\\\\?\\") + cwd_str.get()[0] + L":\\beyond-toplevel"); |
||||
|
||||
// Absolute unix paths lack drive letters, driveless absolute windows paths
|
||||
// do too. Neither can be converted to a drive-specifying absolute Windows
|
||||
// path.
|
||||
ASSERT_EQ(testonly_utf8_to_winpath("/absolute/unix/path"), L""); |
||||
// Though valid on Windows, we also don't support UNC paths (\\UNC\\blah).
|
||||
ASSERT_EQ(testonly_utf8_to_winpath("\\driveless\\absolute"), L""); |
||||
// Though valid in cmd.exe, drive-relative paths are not supported.
|
||||
ASSERT_EQ(testonly_utf8_to_winpath("c:foo"), L""); |
||||
ASSERT_EQ(testonly_utf8_to_winpath("c:/foo"), L"\\\\?\\c:\\foo"); |
||||
ASSERT_EQ(testonly_utf8_to_winpath("\\\\?\\C:\\foo"), L"\\\\?\\C:\\foo"); |
||||
} |
||||
|
||||
TEST_F(IoWin32Test, Utf8Utf16ConversionTest) { |
||||
string mbs; |
||||
wstring wcs; |
||||
ASSERT_TRUE(strings::utf8_to_wcs(kUtf8Text, &wcs)); |
||||
ASSERT_TRUE(strings::wcs_to_utf8(kUtf16Text, &mbs)); |
||||
ASSERT_EQ(wcs, kUtf16Text); |
||||
ASSERT_EQ(mbs, kUtf8Text); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace win32
|
||||
} // namespace internal
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#endif // defined(_WIN32)
|
||||
|
Loading…
Reference in new issue