Added support for paths containing unicode characters on Windows (#921)

Co-authored-by: Christophe Greisberger <christophe@greisberger.fr>
Co-authored-by: an-tao <antao2002@gmail.com>
Co-authored-by: marty1885 <marty188586@gmail.com>
This commit is contained in:
Greisberger Christophe 2021-07-12 16:38:02 +02:00 committed by GitHub
parent f87a6ca1b8
commit d888816997
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 322 additions and 181 deletions

2
.gitignore vendored
View File

@ -46,4 +46,4 @@ latex/
CMakeSettings.json
install
trace.json
.cache/
.cache/

View File

@ -91,9 +91,10 @@ include(CheckIncludeFileCXX)
check_include_file_cxx(any HAS_ANY)
check_include_file_cxx(string_view HAS_STRING_VIEW)
check_include_file_cxx(coroutine HAS_COROUTINE)
if (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)
check_include_file_cxx(filesystem HAS_FILESYSTEM)
if (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE AND HAS_FILESYSTEM)
set(DROGON_CXX_STANDARD 20)
elseif (HAS_ANY AND HAS_STRING_VIEW)
elseif (HAS_ANY AND HAS_STRING_VIEW AND HAS_FILESYSTEM)
set(DROGON_CXX_STANDARD 17)
else ()
set(DROGON_CXX_STANDARD 14)
@ -128,14 +129,33 @@ else (NOT WIN32)
endif (NOT WIN32)
if (DROGON_CXX_STANDARD EQUAL 14)
# With C++14, use boost to support any and string_view
# With C++14, use Boost to support any and string_view
message(STATUS "use c++14")
find_package(Boost 1.61.0 REQUIRED)
message(STATUS "boost include dir:" ${Boost_INCLUDE_DIR})
find_package(Boost 1.61.0 REQUIRED COMPONENTS filesystem)
message(STATUS "Using Boost filesytem, string_view and any")
message(STATUS "Boost include dir: " ${Boost_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::boost)
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::filesystem)
list(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW ${Boost_INCLUDE_DIR})
elseif (DROGON_CXX_STANDARD EQUAL 17)
# With C++17, use Boost if std::filesystem::path is missing
message(STATUS "use c++17")
# Check for partial implementation of c++17 (Windows/OSX only?)
try_compile(check_filesystem_path ${CMAKE_BINARY_DIR}/cmaketest
${PROJECT_SOURCE_DIR}/cmake/tests/check_has_std_filesystem_path.cc
CXX_STANDARD 17)
if (check_filesystem_path)
message(STATUS "Using c++17 filesytem::path")
else()
find_package(Boost 1.49.0 COMPONENTS filesystem system REQUIRED)
message(STATUS "Using Boost filesytem::path")
message(STATUS "Boost include dir: " ${Boost_INCLUDE_DIR})
include_directories(${BOOST_INCLUDE_DIRS})
message(STATUS "Boost libraries: " ${Boost_LIBRARIES})
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::filesystem Boost::system)
list(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW ${Boost_INCLUDE_DIR})
endif()
else ()
message(STATUS "use c++20")
endif ()

View File

@ -0,0 +1,7 @@
#include <filesystem>
int main()
{
std::filesystem::path aPath("../");
return 0;
}

View File

@ -32,10 +32,12 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
# ##############################################################################
if (CMAKE_CXX_STANDARD LESS 17)
# With C++14, use boost to support any and string_view
# With C++14, use boost to support any, string_view and filesystem
message(STATUS "use c++14")
find_package(Boost 1.61.0 REQUIRED)
target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
find_package(Boost 1.61.0 REQUIRED COMPONENTS filesystem)
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::boost)
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::filesystem)
elseif (CMAKE_CXX_STANDARD LESS 20)
message(STATUS "use c++17")
else ()

View File

@ -154,6 +154,39 @@ DROGON_EXPORT std::string formattedString(const char *format, ...);
*/
DROGON_EXPORT int createPath(const std::string &path);
#ifdef _WIN32
/**
* @brief Convert a UTF-8 path with arbitrary directory separator to a standard
* Windows UCS2 path.
* @note Although windows accept both slash and backslash as directory
* separator, it is better to stick to its standard.
*
* @param strUtf8Path Ascii path considered as being UTF-8
*
* @return std::wstring path, with windows standard backslash directory
* separator.
*/
DROGON_EXPORT std::wstring toNativePath(const std::string &strPath);
/**
* @brief Convert a UCS2 to an UTF-8 path.
*
* @param strPath Wide char unicode path
*
* @return std::string path in UTF-8 unicode, with standard '/' directory
* separator.
*/
DROGON_EXPORT std::string fromNativePath(std::wstring strPath);
#else // _WIN32
inline const std::string &toNativePath(const std::string &strPath)
{
return strPath;
}
inline const std::string &fromNativePath(const std::string &strPath)
{
return strPath;
}
#endif // _WIN32
/// Replace all occurances of from to to inplace
/**
* @param from string to replace

View File

@ -16,6 +16,7 @@
#include <trantor/utils/Logger.h>
#ifdef _WIN32
#include <mman.h>
#include <drogon/utils/Utilities.h>
#else
#include <unistd.h>
#include <sys/mman.h>
@ -29,7 +30,8 @@ CacheFile::CacheFile(const std::string &path, bool autoDelete)
#ifndef _MSC_VER
file_ = fopen(path_.data(), "wb+");
#else
if (fopen_s(&file_, path_.data(), "wb+") != 0)
auto wPath{drogon::utils::toNativePath(path)};
if (_wfopen_s(&file_, wPath.c_str(), L"wb+") != 0)
{
file_ = nullptr;
}
@ -46,7 +48,8 @@ CacheFile::~CacheFile()
{
fclose(file_);
#ifdef _WIN32
_unlink(path_.data());
auto wPath{drogon::utils::toNativePath(path_)};
_wunlink(wPath.c_str());
#else
unlink(path_.data());
#endif

View File

@ -22,9 +22,14 @@
#include <trantor/utils/Logger.h>
#ifndef _WIN32
#include <unistd.h>
#define os_access access
#else
#include <io.h>
#define os_access _waccess
#define R_OK 04
#define W_OK 02
#endif
#include <drogon/utils/Utilities.h>
using namespace drogon;
static bool bytesSize(std::string &sizeStr, size_t &size)
@ -94,27 +99,20 @@ static bool bytesSize(std::string &sizeStr, size_t &size)
}
ConfigLoader::ConfigLoader(const std::string &configFile)
{
#ifdef _WIN32
if (_access(configFile.c_str(), 0) != 0)
#else
if (access(configFile.c_str(), 0) != 0)
#endif
if (os_access(drogon::utils::toNativePath(configFile).c_str(), 0) != 0)
{
std::cerr << "Config file " << configFile << " not found!" << std::endl;
exit(1);
}
#ifdef _WIN32
if (_access(configFile.c_str(), 04) != 0)
#else
if (access(configFile.c_str(), R_OK) != 0)
#endif
if (os_access(drogon::utils::toNativePath(configFile).c_str(), R_OK) != 0)
{
std::cerr << "No permission to read config file " << configFile
<< std::endl;
exit(1);
}
configFile_ = configFile;
std::ifstream infile(configFile.c_str(), std::ifstream::in);
std::ifstream infile(drogon::utils::toNativePath(configFile).c_str(),
std::ifstream::in);
if (infile)
{
try

View File

@ -40,6 +40,7 @@
#include <drogon/HttpTypes.h>
#include <drogon/Session.h>
#include <drogon/utils/Utilities.h>
#include "filesystem.h"
#include <trantor/utils/AsyncFileLogger.h>
#include <json/json.h>
@ -58,8 +59,12 @@
#include <sys/file.h>
#include <uuid.h>
#include <unistd.h>
#define os_access access
#else
#include <io.h>
#define os_access _waccess
#define R_OK 04
#define W_OK 02
#endif
using namespace drogon;
@ -393,20 +398,14 @@ HttpAppFramework &HttpAppFrameworkImpl::setLogPath(
{
if (logPath.empty())
return *this;
#ifdef _WIN32
if (_access(logPath.c_str(), 0) != 0)
#else
if (access(logPath.c_str(), 0) != 0)
#endif
// std::filesystem does not provide a method to check access permissions, so
// keep existing code
if (os_access(utils::toNativePath(logPath).c_str(), 0) != 0)
{
std::cerr << "Log path does not exist!\n";
exit(1);
}
#ifdef _WIN32
if (_access(logPath.c_str(), 06) != 0)
#else
if (access(logPath.c_str(), R_OK | W_OK) != 0)
#endif
if (os_access(utils::toNativePath(logPath).c_str(), R_OK | W_OK) != 0)
{
std::cerr << "Unable to access log path!\n";
exit(1);
@ -493,11 +492,9 @@ void HttpAppFrameworkImpl::run()
// set logger
if (!logPath_.empty())
{
#ifdef _WIN32
if (_access(logPath_.c_str(), 06) != 0)
#else
if (access(logPath_.c_str(), R_OK | W_OK) != 0)
#endif
// std::filesystem does not provide a method to check access
// permissions, so keep existing code
if (os_access(utils::toNativePath(logPath_).c_str(), R_OK | W_OK) != 0)
{
LOG_ERROR << "log file path not exist";
abort();
@ -666,22 +663,14 @@ HttpAppFramework &HttpAppFrameworkImpl::setUploadPath(
const std::string &uploadPath)
{
assert(!uploadPath.empty());
if (uploadPath[0] == '/' ||
(uploadPath.length() >= 2 && uploadPath[0] == '.' &&
uploadPath[1] == '/') ||
(uploadPath.length() >= 3 && uploadPath[0] == '.' &&
uploadPath[1] == '.' && uploadPath[2] == '/') ||
uploadPath == "." || uploadPath == "..")
filesystem::path fsUploadPath(utils::toNativePath(uploadPath));
if (!fsUploadPath.is_absolute())
{
uploadPath_ = uploadPath;
}
else
{
if (rootPath_[rootPath_.length() - 1] == '/')
uploadPath_ = rootPath_ + uploadPath;
else
uploadPath_ = rootPath_ + "/" + uploadPath;
filesystem::path fsRoot(utils::toNativePath(rootPath_));
fsUploadPath = fsRoot / fsUploadPath;
}
uploadPath_ = utils::fromNativePath(fsUploadPath.native());
return *this;
}
void HttpAppFrameworkImpl::findSessionForRequest(const HttpRequestImplPtr &req)

View File

@ -17,14 +17,16 @@
#include <drogon/MultiPart.h>
#include <fstream>
#include <iostream>
// Switch between native c++17 or boost for c++14
#ifdef HAS_STD_FILESYSTEM_PATH
#include <system_error>
namespace stl = std;
#else // HAS_STD_FILESYSTEM_PATH
#include <boost/system/error_code.hpp>
namespace stl = boost::system;
#endif // HAS_STD_FILESYSTEM_PATH
using namespace drogon;
// Verify if last char of path is a slash, otherwise, add the slash
static inline void ensureSlashPostfix(std::string &path)
{
if (path[path.length() - 1] != '/')
path += '/';
}
int HttpFileImpl::save() const
{
@ -36,59 +38,43 @@ int HttpFileImpl::save(const std::string &path) const
assert(!path.empty());
if (fileName_.empty())
return -1;
std::string fileName;
if (path[0] == '/' ||
(path.length() >= 2 && path[0] == '.' && path[1] == '/') ||
(path.length() >= 3 && path[0] == '.' && path[1] == '.' &&
path[2] == '/') ||
path == "." || path == "..")
filesystem::path fsPath(utils::toNativePath(path));
if (!fsPath.is_absolute() &&
(!fsPath.has_parent_path() ||
(fsPath.begin()->string() != "." && fsPath.begin()->string() != "..")))
{
// Absolute or relative path
fileName = path;
filesystem::path fsUploadPath(utils::toNativePath(
HttpAppFrameworkImpl::instance().getUploadPath()));
fsPath = fsUploadPath / fsPath;
}
else
{
fileName = HttpAppFrameworkImpl::instance().getUploadPath();
ensureSlashPostfix(fileName);
fileName += path;
}
if (utils::createPath(fileName) < 0)
return -1;
ensureSlashPostfix(fileName);
fileName += fileName_;
return saveTo(fileName);
filesystem::path fsFileName(utils::toNativePath(fileName_));
return saveTo(fsPath / fsFileName);
}
int HttpFileImpl::saveAs(const std::string &fileName) const
{
assert(!fileName.empty());
std::string pathAndFileName;
if (fileName[0] == '/' ||
(fileName.length() >= 2 && fileName[0] == '.' && fileName[1] == '/') ||
(fileName.length() >= 3 && fileName[0] == '.' && fileName[1] == '.' &&
fileName[2] == '/'))
filesystem::path fsFileName(utils::toNativePath(fileName));
if (!fsFileName.is_absolute() && (!fsFileName.has_parent_path() ||
(fsFileName.begin()->string() != "." &&
fsFileName.begin()->string() != "..")))
{
// Absolute or relative path
pathAndFileName = fileName;
filesystem::path fsUploadPath(utils::toNativePath(
HttpAppFrameworkImpl::instance().getUploadPath()));
fsFileName = fsUploadPath / fsFileName;
}
else
if (fsFileName.has_parent_path())
{
pathAndFileName = HttpAppFrameworkImpl::instance().getUploadPath();
ensureSlashPostfix(pathAndFileName);
pathAndFileName += fileName;
}
auto pathPos = pathAndFileName.rfind('/');
if (pathPos != std::string::npos)
{
std::string path = pathAndFileName.substr(0, pathPos);
if (utils::createPath(path) < 0)
stl::error_code err;
filesystem::create_directories(fsFileName.parent_path(), err);
if (err)
return -1;
}
return saveTo(pathAndFileName);
return saveTo(fsFileName);
}
int HttpFileImpl::saveTo(const std::string &pathAndFileName) const
int HttpFileImpl::saveTo(const filesystem::path &pathAndFileName) const
{
LOG_TRACE << "save uploaded file:" << pathAndFileName;
std::ofstream file(pathAndFileName, std::ios::binary);
std::ofstream file(pathAndFileName.native(), std::ios::binary);
if (file.is_open())
{
file.write(fileContent_.data(), fileContent_.size());
@ -101,6 +87,7 @@ int HttpFileImpl::saveTo(const std::string &pathAndFileName) const
return -1;
}
}
std::string HttpFileImpl::getMd5() const
{
return utils::getMd5(fileContent_.data(), fileContent_.size());

View File

@ -15,12 +15,14 @@
#pragma once
#include "HttpUtils.h"
#include <drogon/utils/string_view.h>
#include "filesystem.h"
#include <drogon/HttpRequest.h>
#include <map>
#include <string>
#include <vector>
#include <memory>
namespace drogon
{
class HttpFileImpl
@ -110,7 +112,8 @@ class HttpFileImpl
/// Return the md5 string of the file
std::string getMd5() const;
int saveTo(const std::string &pathAndFileName) const;
// int saveTo(const std::string &pathAndFileName) const;
int saveTo(const filesystem::path &pathAndFileName) const;
void setRequest(const HttpRequestPtr &req)
{
requestPtr_ = req;
@ -122,4 +125,4 @@ class HttpFileImpl
string_view fileContent_;
HttpRequestPtr requestPtr_;
};
} // namespace drogon
} // namespace drogon

View File

@ -287,7 +287,8 @@ void HttpRequestImpl::appendToBuffer(trantor::MsgBuffer *output) const
content.append("\"; filename=\"");
content.append(file.fileName());
content.append("\"\r\n\r\n");
std::ifstream infile(file.path(), std::ifstream::binary);
std::ifstream infile(utils::toNativePath(file.path()),
std::ifstream::binary);
if (!infile)
{
LOG_ERROR << file.path() << " not found";

View File

@ -17,14 +17,19 @@
#include "HttpUtils.h"
#include <drogon/HttpViewData.h>
#include <drogon/IOThreadStorage.h>
#include "filesystem.h"
#include <fstream>
#include <memory>
#include <cstdio>
#include <sys/stat.h>
#include <trantor/utils/Logger.h>
#ifdef _WIN32
#define stat _stati64
// Switch between native c++17 or boost for c++14
#ifdef HAS_STD_FILESYSTEM_PATH
namespace stl = std;
#else
namespace stl = boost::system;
#endif
using namespace trantor;
using namespace drogon;
@ -235,7 +240,7 @@ HttpResponsePtr HttpResponse::newFileResponse(
const std::string &attachmentFileName,
ContentType type)
{
std::ifstream infile(fullPath, std::ifstream::binary);
std::ifstream infile(utils::toNativePath(fullPath), std::ifstream::binary);
LOG_TRACE << "send http file:" << fullPath;
if (!infile)
{
@ -345,19 +350,19 @@ void HttpResponseImpl::makeHeaderString(trantor::MsgBuffer &buffer)
}
else
{
struct stat filestat
stl::error_code err;
filesystem::path fsSendfile(utils::toNativePath(sendfileName_));
auto fileSize = filesystem::file_size(fsSendfile, err);
if (err)
{
};
if (stat(sendfileName_.data(), &filestat) < 0)
{
LOG_SYSERR << sendfileName_ << " stat error";
LOG_SYSERR << fsSendfile << " stat error " << err.value()
<< ": " << err.message();
return;
}
len = snprintf(
buffer.beginWrite(),
buffer.writableBytes(),
contentLengthFormatString<decltype(filestat.st_size)>(),
filestat.st_size);
len = snprintf(buffer.beginWrite(),
buffer.writableBytes(),
contentLengthFormatString<decltype(fileSize)>(),
fileSize);
}
buffer.hasWritten(len);
if (headers_.find("connection") == headers_.end())

View File

@ -23,11 +23,20 @@
#ifndef _WIN32
#include <sys/file.h>
#else
#define stat _stati64
#define stat _wstati64
#define S_ISREG(m) (((m)&0170000) == (0100000))
#define S_ISDIR(m) (((m)&0170000) == (0040000))
#endif
#include <sys/stat.h>
// Switch between native c++17 or boost for c++14
#include "filesystem.h"
#ifdef HAS_STD_FILESYSTEM_PATH
#include <system_error>
namespace stl = std;
#else
#include <boost/system/error_code.hpp>
namespace stl = boost::system;
#endif
using namespace drogon;
@ -137,13 +146,14 @@ void StaticFileRouter::route(
std::string filePath =
location.realLocation_ +
std::string{restOfThePath.data(), restOfThePath.length()};
struct stat fileStat;
if (stat(filePath.c_str(), &fileStat) != 0)
filesystem::path fsFilePath(utils::toNativePath(filePath));
stl::error_code err;
if (!filesystem::exists(fsFilePath, err))
{
defaultHandler_(req, std::move(callback));
return;
}
if (S_ISDIR(fileStat.st_mode))
if (filesystem::is_directory(fsFilePath, err))
{
// Check if path is eligible for an implicit index.html
if (implicitPageEnable_)
@ -215,10 +225,11 @@ void StaticFileRouter::route(
std::string directoryPath =
HttpAppFrameworkImpl::instance().getDocumentRoot() + path;
struct stat fileStat;
if (stat(directoryPath.c_str(), &fileStat) == 0)
filesystem::path fsDirectoryPath(utils::toNativePath(directoryPath));
stl::error_code err;
if (filesystem::exists(fsDirectoryPath, err))
{
if (S_ISDIR(fileStat.st_mode))
if (filesystem::is_directory(fsDirectoryPath, err))
{
// Check if path is eligible for an implicit index.html
if (implicitPageEnable_)
@ -299,9 +310,15 @@ void StaticFileRouter::sendStaticFileResponse(
}
else
{
struct stat fileStat;
LOG_TRACE << "enabled LastModify";
if (stat(filePath.c_str(), &fileStat) == 0 &&
// std::filesystem::file_time_type::clock::to_time_t still not
// implemented by M$, even in c++20, so keep calls to stat()
#ifdef _WIN32
struct _stati64 fileStat;
#else // _WIN32
struct stat fileStat;
#endif // _WIN32
if (stat(utils::toNativePath(filePath).c_str(), &fileStat) == 0 &&
S_ISREG(fileStat.st_mode))
{
fileExists = true;
@ -361,9 +378,10 @@ void StaticFileRouter::sendStaticFileResponse(
}
if (!fileExists)
{
struct stat fileStat;
if (stat(filePath.c_str(), &fileStat) != 0 ||
!S_ISREG(fileStat.st_mode))
filesystem::path fsFilePath(utils::toNativePath(filePath));
stl::error_code err;
if (!filesystem::exists(fsFilePath, err) ||
!filesystem::is_regular_file(fsFilePath, err))
{
defaultHandler_(req, std::move(callback));
return;
@ -383,9 +401,10 @@ void StaticFileRouter::sendStaticFileResponse(
{
// Find compressed file first.
auto brFileName = filePath + ".br";
struct stat filestat;
if (stat(brFileName.c_str(), &filestat) == 0 &&
S_ISREG(filestat.st_mode))
filesystem::path fsBrFile(utils::toNativePath(brFileName));
stl::error_code err;
if (filesystem::exists(fsBrFile, err) &&
filesystem::is_regular_file(fsBrFile, err))
{
resp =
HttpResponse::newFileResponse(brFileName,
@ -399,9 +418,10 @@ void StaticFileRouter::sendStaticFileResponse(
{
// Find compressed file first.
auto gzipFileName = filePath + ".gz";
struct stat filestat;
if (stat(gzipFileName.c_str(), &filestat) == 0 &&
S_ISREG(filestat.st_mode))
filesystem::path fsGzipFile(utils::toNativePath(gzipFileName));
stl::error_code err;
if (filesystem::exists(fsGzipFile, err) &&
filesystem::is_regular_file(fsGzipFile, err))
{
resp =
HttpResponse::newFileResponse(gzipFileName,

View File

@ -13,6 +13,7 @@
*/
#include <drogon/utils/Utilities.h>
#include "filesystem.h"
#include <trantor/utils/Logger.h>
#include <drogon/config.h>
#ifdef OpenSSL_FOUND
@ -51,6 +52,15 @@
#include <fcntl.h>
#include <stdarg.h>
// Switch between native c++17 or boost for c++14
#ifdef HAS_STD_FILESYSTEM_PATH
#include <system_error>
namespace stl = std;
#else
#include <boost/system/error_code.hpp>
namespace stl = boost::system;
#endif
#ifdef _WIN32
char *strptime(const char *s, const char *f, struct tm *tm)
{
@ -1043,61 +1053,70 @@ std::string formattedString(const char *format, ...)
return strBuffer;
}
#ifdef _WIN32
std::string utf8_encode(const std::wstring &wstr)
{
if (wstr.empty())
return {};
int nSizeNeeded = ::WideCharToMultiByte(
CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
std::string strTo(nSizeNeeded, 0);
::WideCharToMultiByte(CP_UTF8,
0,
&wstr[0],
(int)wstr.size(),
&strTo[0],
nSizeNeeded,
NULL,
NULL);
return strTo;
}
std::wstring utf8_decode(const std::string &str)
{
if (str.empty())
return {};
int nSizeNeeded =
::MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
std::wstring wstrTo(nSizeNeeded, 0);
::MultiByteToWideChar(
CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], nSizeNeeded);
return wstrTo;
}
std::wstring toNativePath(const std::string &strUtf8Path)
{
// Consider path to be utf-8, to allow using paths with unicode characters
auto wPath{utf8_decode(strUtf8Path)};
// Not needed: normalize path (just replaces '/' with '\')
filesystem::path fsPath(wPath);
// Not needed: normalize path (just replaces '/' with '\')
fsPath.make_preferred();
return fsPath.native();
}
std::string fromNativePath(std::wstring wstrPath)
{
std::replace(wstrPath.begin(), wstrPath.end(), L'\\', L'/');
auto strPath{utf8_encode(wstrPath)};
return strPath;
}
#endif // _WIN32
int createPath(const std::string &path)
{
auto tmpPath = path;
std::stack<std::string> pathStack;
#ifdef _WIN32
while (_access(tmpPath.c_str(), 06) != 0)
#else
while (access(tmpPath.c_str(), F_OK) != 0)
#endif
if (path.empty())
return 0;
auto osPath{toNativePath(path)};
if (osPath.back() != filesystem::path::preferred_separator)
osPath.push_back(filesystem::path::preferred_separator);
filesystem::path fsPath(osPath);
stl::error_code err;
filesystem::create_directories(fsPath, err);
if (err)
{
if (tmpPath == "./" || tmpPath == "/")
return -1;
while (tmpPath[tmpPath.length() - 1] == '/')
tmpPath.resize(tmpPath.length() - 1);
auto pos = tmpPath.rfind('/');
if (pos != std::string::npos)
{
pathStack.push(tmpPath.substr(pos));
tmpPath = tmpPath.substr(0, pos + 1);
}
else
{
pathStack.push(tmpPath);
tmpPath.clear();
break;
}
}
while (pathStack.size() > 0)
{
if (tmpPath.empty())
{
tmpPath = pathStack.top();
}
else
{
if (tmpPath[tmpPath.length() - 1] == '/')
{
tmpPath.append(pathStack.top());
}
else
{
tmpPath.append("/").append(pathStack.top());
}
}
pathStack.pop();
#ifdef _WIN32
if (_mkdir(tmpPath.c_str()) == -1)
#else
if (mkdir(tmpPath.c_str(), 0755) == -1)
#endif
{
LOG_ERROR << "Can't create path:" << path;
return -1;
}
LOG_ERROR << "Error " << err.value() << " creating path " << osPath
<< ": " << err.message();
return -1;
}
return 0;
}

54
lib/src/filesystem.h Normal file
View File

@ -0,0 +1,54 @@
/* @author An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#pragma once
/* Check of std::filesystem::path availability:
* - OS X: depends on the target OSX version (>= 10.15 Catalina)
* - Windows: Visual Studio >= 2019 (c++20)
* - Others: should already have it in c++17
*/
#if (defined(__APPLE__) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101500L) || \
(defined(_WIN32) && defined(_MSC_VER) && _MSC_VER >= 1920) || \
(!defined(__APPLE__) && !defined(_WIN32) && __cplusplus >= 201703L)
#define HAS_STD_FILESYSTEM_PATH
#endif
#ifdef HAS_STD_FILESYSTEM_PATH
#include <filesystem>
#else
#include <boost/filesystem.hpp>
#endif
namespace drogon
{
#ifdef HAS_STD_FILESYSTEM_PATH
namespace filesystem = std::filesystem;
#else
namespace filesystem = boost::filesystem;
#endif
} // namespace drogon
namespace trantor
{
inline LogStream &operator<<(LogStream &ls, const drogon::filesystem::path &v)
{
#if defined(_WIN32) && defined(__cpp_char8_t)
// Convert UCS-2 to UTF-8, not ASCII - not needed on other OSes
auto u8path{v.u8string()};
ls.append((const char *)u8path.data(), u8path.length());
#else
// No need to convert
ls.append(v.string().data(), v.string().length());
#endif
return ls;
}
} // namespace trantor

@ -1 +1 @@
Subproject commit 15556700e5ce305c7d3fd5a970a58ef6b8297e0c
Subproject commit d7f700ac7ca0b6c656755142378bc71cd34ecfed