From eb6f0f710c9089cf327fbeb967542a59c8a0299e Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Tue, 14 Apr 2020 12:12:34 +0200 Subject: [PATCH 1/3] Correctly detetect the dataDir on windows. We must use the wide version of the getenv to correctly handle the case we have accents in the user directory. This also change the default dataDirectory on windows from $APPDATA to $APPDATA/kiwix. --- src/tools/pathTools.cpp | 45 ++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/src/tools/pathTools.cpp b/src/tools/pathTools.cpp index b4381789a..1cf270611 100644 --- a/src/tools/pathTools.cpp +++ b/src/tools/pathTools.cpp @@ -364,30 +364,47 @@ std::string getCurrentDirectory() std::string getDataDirectory() { +// Try to get the dataDir from the `KIWIX_DATA_DIR` env var #ifdef _WIN32 - char* cDataDir = ::getenv("APPDATA"); + wchar_t* cDataDir = ::_wgetenv(L"KIWIX_DATA_DIR"); + if (cDataDir != nullptr) { + return WideToUtf8(cDataDir); + } #else char* cDataDir = ::getenv("KIWIX_DATA_DIR"); -#endif - std::string dataDir = cDataDir==nullptr ? "" : cDataDir; - if (!dataDir.empty()) { - return dataDir; + if (cDataDir != nullptr) { + return cDataDir; } +#endif + +// Compute the dataDir from the user directory. + std::string dataDir; #ifdef _WIN32 - cDataDir = ::getenv("USERPROFILE"); - dataDir = cDataDir==nullptr ? getCurrentDirectory() : cDataDir; + cDataDir = ::_wgetenv(L"APPDATA"); + if (cDataDir == nullptr) + cDataDir = ::_wgetenv(L"USERPROFILE"); + if (cDataDir != nullptr) + dataDir = WideToUtf8(cDataDir); #else cDataDir = ::getenv("XDG_DATA_HOME"); - dataDir = cDataDir==nullptr ? "" : cDataDir; - if (dataDir.empty()) { + if (cDataDir != nullptr) { + dataDir = cDataDir; + } else { cDataDir = ::getenv("HOME"); - dataDir = cDataDir==nullptr ? getCurrentDirectory() : cDataDir; - dataDir = appendToDirectory(dataDir, ".local"); - dataDir = appendToDirectory(dataDir, "share"); + if (cDataDir != nullptr) { + dataDir = cDataDir; + dataDir = appendToDirectory(dataDir, ".local"); + dataDir = appendToDirectory(dataDir, "share"); + } } #endif - auto ret = appendToDirectory(dataDir, "kiwix"); - return ret; + if (!dataDir.empty()) { + dataDir = appendToDirectory(dataDir, "kiwix"); + return dataDir; + } + +// Let's use the currentDirectory + return getCurrentDirectory(); } static std::map extMimeTypes = { From 4c8aad0e68ea9611340dff8b42ccf155ff85194e Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Tue, 14 Apr 2020 15:48:31 +0200 Subject: [PATCH 2/3] Do not use std::fstream has it doesn't support wchar path. This is surprising, but C++11 fstream doesn't have a constructor that take wchar as path. So, on windows, we cannot open a stream on a path containing non ascii char. VC++ provide an extension for that, but it is not standard and g++ mingwin doesn't provide it. So move all our write/read tools function to the plain old c versions, using _wopen to open wide path on windows. --- src/tools/pathTools.cpp | 71 ++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/src/tools/pathTools.cpp b/src/tools/pathTools.cpp index 1cf270611..c470c4a18 100644 --- a/src/tools/pathTools.cpp +++ b/src/tools/pathTools.cpp @@ -18,6 +18,7 @@ */ #include "tools/pathTools.h" +#include #ifdef __APPLE__ #include @@ -42,12 +43,16 @@ #include #ifdef _WIN32 - #define SEPARATOR "\\" +# define SEPARATOR "\\" +# include #else - #define SEPARATOR "/" -#include +# define SEPARATOR "/" +# include +# include #endif +#include + #include #ifndef PATH_MAX @@ -227,14 +232,33 @@ std::string getFileSizeAsString(const std::string& path) std::string getFileContent(const std::string& path) { - std::ifstream f(path, std::ios::in|std::ios::ate); +#ifdef _WIN32 + auto wpath = Utf8ToWide(path); + auto fd = _wopen(wpath.c_str(), _O_RDONLY | _O_BINARY); +#else + auto fd = open(path.c_str(), O_RDONLY); +#endif std::string content; - if (f.is_open()) { - auto size = f.tellg(); - content.reserve(size); - f.seekg(0, std::ios::beg); - content.assign((std::istreambuf_iterator(f)), - std::istreambuf_iterator()); + if (fd != -1) { +#ifdef _WIN32 + auto size = _lseeki64(fd, 0, SEEK_END); +#else + auto size = lseek(fd, 0, SEEK_END); +#endif + content.resize(size); +#ifdef _WIN32 + _lseeki64(fd, 0, SEEK_SET); +#else + lseek(fd, 0, SEEK_SET); +#endif + auto p = (char*)content.data(); + while (size) { + auto readsize = size > 2048 ? 2048 : size; + readsize = ::read(fd, p, readsize); + p += readsize; + size -= readsize; + } + close(fd); } return content; } @@ -287,22 +311,22 @@ std::string makeTmpDirectory() /* Try to create a link and if does not work then make a copy */ bool copyFile(const std::string& sourcePath, const std::string& destPath) { +#ifdef _WIN32 + return CopyFileW(Utf8ToWide(sourcePath).c_str(), Utf8ToWide(destPath).c_str(), 1); +#else try { -#ifndef _WIN32 if (link(sourcePath.c_str(), destPath.c_str()) != 0) { -#endif std::ifstream infile(sourcePath.c_str(), std::ios_base::binary); std::ofstream outfile(destPath.c_str(), std::ios_base::binary); outfile << infile.rdbuf(); -#ifndef _WIN32 } -#endif } catch (std::exception& e) { std::cerr << e.what() << std::endl; return false; } return true; +#endif } std::string getExecutablePath(bool realPathOnly) @@ -341,10 +365,21 @@ std::string getExecutablePath(bool realPathOnly) bool writeTextFile(const std::string& path, const std::string& content) { - std::ofstream file; - file.open(path.c_str()); - file << content; - file.close(); +#ifdef _WIN32 + auto wpath = Utf8ToWide(path); + auto fd = _wopen(wpath.c_str(), _O_WRONLY | _O_CREAT | _O_TRUNC, S_IWRITE); +#else + auto fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +#endif + if (fd == -1) + return false; + + if (write(fd, content.c_str(), content.size()) != (long)content.size()) { + close(fd); + return false; + } + close(fd); return true; } From 9d8bf8ddcb8dfa8a4425a36a4536a6a1acb08062 Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Wed, 15 Apr 2020 08:24:55 +0200 Subject: [PATCH 3/3] Create the dataDirectory before returning its path. --- src/tools/pathTools.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/pathTools.cpp b/src/tools/pathTools.cpp index c470c4a18..e3adca8fa 100644 --- a/src/tools/pathTools.cpp +++ b/src/tools/pathTools.cpp @@ -435,6 +435,7 @@ std::string getDataDirectory() #endif if (!dataDir.empty()) { dataDir = appendToDirectory(dataDir, "kiwix"); + makeDirectory(dataDir); return dataDir; }