diff --git a/C/c4BlobStore.cc b/C/c4BlobStore.cc index 6043f70d10..681ae49070 100644 --- a/C/c4BlobStore.cc +++ b/C/c4BlobStore.cc @@ -91,7 +91,7 @@ static optional BlobKeyFromFilename(slice filename) { C4BlobStore::C4BlobStore(slice dirPath, C4DatabaseFlags flags, const C4EncryptionKey& key) : _dirPath(dirPath), _flags(flags), _encryptionKey(key) { - FilePath dir(_dirPath, ""); + FilePath dir(_dirPath); if ( dir.exists() ) { dir.mustExistAsDir(); } else if ( !(flags & kC4DB_ReadOnly) ) { @@ -103,9 +103,9 @@ C4BlobStore::~C4BlobStore() = default; void C4BlobStore::deleteStore() { dir().delRecursive(); } -FilePath C4BlobStore::dir() const { return {_dirPath, ""}; } +FilePath C4BlobStore::dir() const { return filesystem::path(_dirPath); } -FilePath C4BlobStore::pathForKey(C4BlobKey key) const { return {_dirPath, BlobKeyToFilename(key)}; } +FilePath C4BlobStore::pathForKey(C4BlobKey key) const { return filesystem::path(_dirPath) / BlobKeyToFilename(key); } alloc_slice C4BlobStore::getFilePath(C4BlobKey key) const { FilePath path = pathForKey(key); diff --git a/C/c4Database.cc b/C/c4Database.cc index 976eba4a95..96ee72e00c 100644 --- a/C/c4Database.cc +++ b/C/c4Database.cc @@ -27,6 +27,7 @@ #include #include #include +#include // NOTE: Most of C4Database is implemented in its concrete subclass DatabaseImpl. @@ -71,11 +72,12 @@ void C4Database::enableExtension(slice name, slice path) { static FilePath dbPath(slice name, slice parentDir) { if ( name.size == 0 || parentDir.size == 0 ) C4Error::raise(LiteCoreDomain, kC4ErrorInvalidParameter); - return FilePath(string(parentDir), string(name)).addingExtension(kC4DatabaseFilenameExtension); + return FilePath(filesystem::path(parentDir.asString()) / name.asString()) + .addingExtension(kC4DatabaseFilenameExtension); } static void ensureConfigDirExists(const C4DatabaseConfig2& config) { - if ( !(config.flags & kC4DB_ReadOnly) ) (void)FilePath(slice(config.parentDirectory), "").mkdir(); + if ( !(config.flags & kC4DB_ReadOnly) ) (void)FilePath(slice(config.parentDirectory).asString()).mkdir(); } static C4DatabaseConfig newToOldConfig(const C4DatabaseConfig2& config2) { @@ -99,7 +101,7 @@ static C4DatabaseConfig newToOldConfig(const C4DatabaseConfig2& config2) { /*static*/ bool C4Database::deleteAtPath(slice dbPath) { // Find the db file in the bundle: - FilePath bundle{dbPath, ""}; + FilePath bundle(dbPath.asString()); if ( bundle.exists() ) { try { C4StorageEngine storageEngine = nullptr; @@ -143,21 +145,21 @@ constexpr const char* kInvalidDbNameMsgTemplate = /*static*/ Retained C4Database::openAtPath(slice path, C4DatabaseFlags flags, const C4EncryptionKey* key) { C4DatabaseConfig config = {flags}; if ( key ) config.encryptionKey = *key; - return DatabaseImpl::open(FilePath(path, ""), config); + return DatabaseImpl::open(FilePath(path.asString()), config); } /*static*/ void C4Database::copyNamed(slice sourcePath, slice destinationName, const Config& config) { if ( !isValidDbName(destinationName) ) { Warn(kInvalidDbNameMsgTemplate, destinationName.asString().c_str()); } ensureConfigDirExists(config); - FilePath from(sourcePath, ""); + FilePath from(sourcePath.asString()); FilePath to = dbPath(destinationName, config.parentDirectory); C4DatabaseConfig oldConfig = newToOldConfig(config); CopyPrebuiltDB(from, to, &oldConfig); } /*static*/ void C4Database::copyFileToPath(slice sourcePath, slice destinationPath, const C4DatabaseConfig& config) { - return CopyPrebuiltDB(FilePath(sourcePath), FilePath(destinationPath), &config); + return CopyPrebuiltDB(FilePath(sourcePath.asString()), FilePath(destinationPath.asString()), &config); } /*static*/ bool C4Database::deleteNamed(slice name, slice inDirectory) { diff --git a/C/tests/c4DatabaseTest.cc b/C/tests/c4DatabaseTest.cc index d39af0d482..931efc7b5f 100644 --- a/C/tests/c4DatabaseTest.cc +++ b/C/tests/c4DatabaseTest.cc @@ -34,6 +34,7 @@ #include #include #include +#include #include "sqlite3.h" @@ -183,7 +184,7 @@ N_WAY_TEST_CASE_METHOD(C4DatabaseTest, "Database OpenNamed", "[Database][C][!thr REQUIRE(bundle); CHECK(c4db_getName(bundle) == kTestBundleName); C4SliceResult path = c4db_getPath(bundle); - CHECK(path == TEMPDIR("cbl_core_test_bundle.cblite2" kPathSeparator)); // note trailing '/' + CHECK(path == TEMPDIR("cbl_core_test_bundle.cblite2")); c4slice_free(path); REQUIRE(c4db_close(bundle, WITH_ERROR())); c4db_release(bundle); @@ -912,21 +913,22 @@ N_WAY_TEST_CASE_METHOD(C4DatabaseTest, "Database copy", "[Database][C]") { createRev(doc1ID, kRevID, kFleeceBody); createRev(doc2ID, kRevID, kFleeceBody); - string srcPathStr = toString(c4db_getPath(db)); + string srcPathStr = toString(c4db_getPath(db)) + "/"; C4DatabaseConfig2 config = *c4db_getConfig2(db); - string nuPath = string(slice(config.parentDirectory)) + string(kNuName) + ".cblite2" + kPathSeparator; + auto nuPath = filesystem::path(string(slice(config.parentDirectory))) / (string(kNuName) + ".cblite2"); + auto nuPathStr = nuPath.string() + "/"; C4Error error; SECTION("WITH SLASH") {} SECTION("WITHOUT SLASH") { srcPathStr.pop_back(); - nuPath.pop_back(); + nuPathStr.pop_back(); } - if ( !c4db_deleteNamed(kNuName, slice(nuPath), &error) ) { REQUIRE(error.code == 0); } + if ( !c4db_deleteNamed(kNuName, slice(nuPathStr), &error) ) { REQUIRE(error.code == 0); } { ExpectingExceptions x; @@ -1116,8 +1118,8 @@ N_WAY_TEST_CASE_METHOD(C4DatabaseTest, "Database Create Upgrade Fixture", "[.Mai closeDB(); - litecore::FilePath fixturePath(C4DatabaseTest::sFixturesDir + kVersionedFixturesSubDir + filename, ""); - litecore::FilePath(string(path), "").moveToReplacingDir(fixturePath, false); + litecore::FilePath fixturePath(C4DatabaseTest::sFixturesDir + kVersionedFixturesSubDir + filename); + litecore::FilePath(string(path)).moveToReplacingDir(fixturePath, false); C4Log("New fixture is at %s", string(fixturePath).c_str()); } diff --git a/C/tests/c4PerfTest.cc b/C/tests/c4PerfTest.cc index 72fb456dc7..03f12bfec6 100644 --- a/C/tests/c4PerfTest.cc +++ b/C/tests/c4PerfTest.cc @@ -43,7 +43,7 @@ class PerfTest : public C4Test { explicit PerfTest(int variation) : C4Test(variation) { const char* showFastDir = getenv("CBL_SHOWFAST_DIR"); if ( showFastDir ) { - litecore::FilePath showFastPath(showFastDir, ""); + litecore::FilePath showFastPath(showFastDir); if ( showFastPath.exists() && showFastPath.isDir() ) { _showFastDir = showFastDir; } } @@ -251,7 +251,7 @@ class PerfTest : public C4Test { if ( isEncrypted() ) { filename += "_encrypted"; } filename += ".json"; - litecore::FilePath sfPath(_showFastDir, filename); + litecore::FilePath sfPath(filesystem::path(string(_showFastDir)) / filename); ofstream fout(sfPath.path(), ios::trunc | ios::out); fout.exceptions(ostream::failbit | ostream::badbit); @@ -300,7 +300,7 @@ N_WAY_TEST_CASE_METHOD(PerfTest, "Import iTunesMusicLibrary", "[Perf][C][.slow]" st.stop(); st.printReport("******** Importing JSON w/spaces", numDocs, "doc"); - litecore::FilePath path(alloc_slice(c4db_getPath(db)).asString(), "db.sqlite3"); + litecore::FilePath path(filesystem::path(slice(c4db_getPath(db)).asString()) / "db.sqlite3"); fprintf(stderr, "******** DB size is %" PRIi64 "\n", path.dataSize()); reopenDB(); string sf = generateShowfast((double)numDocs / st.elapsed(), "tunes_import_json"); diff --git a/C/tests/c4Test.cc b/C/tests/c4Test.cc index aab9574ac3..64641ab3e4 100644 --- a/C/tests/c4Test.cc +++ b/C/tests/c4Test.cc @@ -304,8 +304,8 @@ void C4Test::deleteAndRecreateDB(C4Database*& db) { /*static*/ alloc_slice C4Test::copyFixtureDB(const string& name) { return copyFixtureDB(sFixturesDir, name); } alloc_slice C4Test::copyFixtureDB(const string& parentDir, const string& name) { - auto srcPath = litecore::FilePath(parentDir + name, ""); - litecore::FilePath destDir(TempDir(), ""); + auto srcPath = litecore::FilePath(parentDir + name); + litecore::FilePath destDir(TempDir()); auto dbPath = destDir[srcPath.fileOrDirName() + "/"]; dbPath.delRecursive(); srcPath.copyTo(dbPath); diff --git a/C/tests/c4Test.hh b/C/tests/c4Test.hh index 52a5a17ef0..28775f9acb 100644 --- a/C/tests/c4Test.hh +++ b/C/tests/c4Test.hh @@ -129,7 +129,7 @@ class WITH_ERROR { // Temporary directory to use for tests. -#define TEMPDIR(PATH) c4str((TempDir() + (PATH)).c_str()) +#define TEMPDIR(PATH) c4str((TempDir() + kPathSeparator + (PATH)).c_str()) const std::string& TempDir(); diff --git a/C/tests/cmake/platform_linux.cmake b/C/tests/cmake/platform_linux.cmake index 8d0a62c164..61c715084a 100644 --- a/C/tests/cmake/platform_linux.cmake +++ b/C/tests/cmake/platform_linux.cmake @@ -2,7 +2,6 @@ function(setup_build) target_sources( C4Tests PRIVATE ${TOP}Crypto/mbedUtils.cc - ${TOP}LiteCore/Unix/strlcat.c ) target_link_libraries( diff --git a/C/tests/cmake/platform_win.cmake b/C/tests/cmake/platform_win.cmake index e06a5ce40d..20f7634330 100644 --- a/C/tests/cmake/platform_win.cmake +++ b/C/tests/cmake/platform_win.cmake @@ -12,9 +12,6 @@ function(setup_build) ${TOP}LiteCore/Support/PlatformIO.cc ${TOP}MSVC/vasprintf-msvc.c ${TOP}MSVC/asprintf.c - ${TOP}MSVC/mkdtemp.cc - ${TOP}MSVC/mkstemp.cc - ${TOP}MSVC/strlcat.c ${TOP}MSVC/asprintf.c ) diff --git a/LiteCore/BlobStore/BlobStreams.cc b/LiteCore/BlobStore/BlobStreams.cc index d8bb811e5c..2b82102030 100644 --- a/LiteCore/BlobStore/BlobStreams.cc +++ b/LiteCore/BlobStore/BlobStreams.cc @@ -15,6 +15,7 @@ #include "EncryptedStream.hh" #include "Error.hh" #include "Logging.hh" +#include namespace litecore { using namespace std; @@ -38,7 +39,7 @@ namespace litecore { BlobWriteStream::BlobWriteStream(const string& blobsDir, EncryptionAlgorithm algorithm, slice encryptionKey) { FILE* file; - _tmpPath = FilePath(blobsDir, "incoming_").mkTempFile(&file); + _tmpPath = FilePath(filesystem::path(blobsDir) / "incoming_").mkTempFile(&file); _writer = shared_ptr{new FileWriteStream(file)}; if ( algorithm != EncryptionAlgorithm::kNoEncryption ) _writer = make_shared(_writer, algorithm, encryptionKey); diff --git a/LiteCore/Database/DatabaseImpl.cc b/LiteCore/Database/DatabaseImpl.cc index 9e15434f09..26cd12a207 100644 --- a/LiteCore/Database/DatabaseImpl.cc +++ b/LiteCore/Database/DatabaseImpl.cc @@ -68,7 +68,7 @@ namespace litecore { // `path` is path to bundle; return value is path to db file. Updates config.storageEngine. */ /*static*/ FilePath DatabaseImpl::findOrCreateBundle(const string& path, bool canCreate, C4StorageEngine& storageEngine) { - FilePath bundle(path, ""); + FilePath bundle(path); bool createdDir = (canCreate && bundle.mkdir()); if ( !createdDir ) bundle.mustExistAsDir(); @@ -118,7 +118,7 @@ namespace litecore { if ( bundlePath.isDir() ) { bundlePath.delRecursive(); } else { - FilePath{bundlePath.path(), ""}.delRecursive(); + FilePath{bundlePath.path()}.delRecursive(); } } } catch ( ... ) { diff --git a/LiteCore/Logging/LogFiles.cc b/LiteCore/Logging/LogFiles.cc index f86474743a..02428e32f0 100644 --- a/LiteCore/Logging/LogFiles.cc +++ b/LiteCore/Logging/LogFiles.cc @@ -128,7 +128,7 @@ namespace litecore { } void purgeOldLogs() { - FilePath logDir(_options.directory, ""); + FilePath logDir(_options.directory); if ( !logDir.existsAsDir() ) { return; } multimap logFiles; diff --git a/LiteCore/Storage/DataFile.cc b/LiteCore/Storage/DataFile.cc index a7252162b0..666e577249 100644 --- a/LiteCore/Storage/DataFile.cc +++ b/LiteCore/Storage/DataFile.cc @@ -20,7 +20,6 @@ #include "Instrumentation.hh" #include #include -#include #include #include #include diff --git a/LiteCore/Support/DatabasePool.cc b/LiteCore/Support/DatabasePool.cc index da9022f00b..d6b36eed31 100644 --- a/LiteCore/Support/DatabasePool.cc +++ b/LiteCore/Support/DatabasePool.cc @@ -86,7 +86,9 @@ namespace litecore { logInfo("...all databases closed!"); } - FilePath DatabasePool::databasePath() const { return FilePath{_dbDir, _dbName + kC4DatabaseFilenameExtension}; } + FilePath DatabasePool::databasePath() const { + return filesystem::path(_dbDir.asString()) / (_dbName + kC4DatabaseFilenameExtension); + } unsigned DatabasePool::capacity() const noexcept { unique_lock lock(_mutex); diff --git a/LiteCore/Support/FilePath.cc b/LiteCore/Support/FilePath.cc index 8353dd357a..2ad903cf3f 100644 --- a/LiteCore/Support/FilePath.cc +++ b/LiteCore/Support/FilePath.cc @@ -16,173 +16,103 @@ #include "Error.hh" #include "StringUtil.hh" #include "PlatformIO.hh" +#include "betterassert.hh" #include // for sqlite3_temp_directory #include #include -#include #include #include #include #include - -#ifndef _MSC_VER -# include -# include -# if __APPLE__ -# include -# include -# elif defined(__linux__) -# include "strlcat.h" -# include -# endif -#else -# include -# include -# include -# include -# include -# include "strlcat.h" -# include "mkstemp.h" -# include "mkdtemp.h" -#endif +#include +#include using namespace std; using namespace fleece; using namespace litecore; -#ifdef __linux__ -static int copyfile(const char* from, const char* to) { - int read_fd, write_fd; - off_t offset = 0; - struct stat stat_buf; - read_fd = open(from, O_RDONLY); - if ( read_fd < 0 ) { return read_fd; } - - if ( fstat(read_fd, &stat_buf) < 0 ) { - int e = errno; - close(read_fd); - errno = e; - return -1; - } +namespace litecore { - write_fd = open(to, O_WRONLY | O_CREAT, stat_buf.st_mode); - if ( write_fd < 0 ) { - int e = errno; - close(read_fd); - errno = e; - return write_fd; + static filesystem::perms perms_from_mode(int mode) { + filesystem::perms perms = filesystem::perms::none; + if ( mode & 0400 ) perms |= filesystem::perms::owner_read; + if ( mode & 0200 ) perms |= filesystem::perms::owner_write; + if ( mode & 0100 ) perms |= filesystem::perms::owner_exec; + if ( mode & 0040 ) perms |= filesystem::perms::group_read; + if ( mode & 0020 ) perms |= filesystem::perms::group_write; + if ( mode & 0010 ) perms |= filesystem::perms::group_exec; + if ( mode & 0004 ) perms |= filesystem::perms::others_read; + if ( mode & 0002 ) perms |= filesystem::perms::others_write; + if ( mode & 0001 ) perms |= filesystem::perms::others_exec; + return perms; } - size_t expected = stat_buf.st_size; - ssize_t bytes = 0; - while ( bytes < expected ) { - expected -= bytes; - bytes = sendfile(write_fd, read_fd, &offset, expected); - if ( bytes < 0 ) { - int e = errno; - close(read_fd); - close(write_fd); - errno = e; - return -1; - } else if ( bytes == 0 ) { - // zero bytes are read. Do we want to try again? Well, let's consider it as an error - Warn("sys/sendfile makes no progress copying %s to %s and we bail out as failure.", from, to); - if ( close(read_fd) < 0 ) { - // take the first errno due to close. - int e = errno; - close(write_fd); - errno = e; - } else { - close(write_fd); - } - return -1; - } - } + static int to_errno(const std::error_code& ec) { + if ( ec.category() == std::generic_category() ) { return ec.value(); } - if ( close(read_fd) < 0 ) { - int e = errno; - close(write_fd); - errno = e; - return -1; + return ec.default_error_condition().value(); } - if ( close(write_fd) < 0 ) { return -1; } - - return 0; -} -#elif defined(_MSC_VER) -typedef HRESULT(WINAPI* CopyFileFunc)(_In_ PCWSTR, _In_ PCWSTR, _In_opt_ COPYFILE2_EXTENDED_PARAMETERS*); - -static int copyfile(const char* from, const char* to) { - CA2WEX<256> wideFrom(from, CP_UTF8); - CA2WEX<256> wideTo(to, CP_UTF8); - int err = CopyFile2(wideFrom, wideTo, nullptr); - - if ( err != S_OK ) { return -1; } - - return 0; -} -#endif - -namespace litecore { + static std::string randomSuffix(size_t len = 12) { + static const char kChars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + thread_local std::mt19937 rng{std::random_device{}()}; + std::uniform_int_distribution dist(0, sizeof(kChars) - 2); + std::string s; + s.reserve(len); + for ( size_t i = 0; i < len; ++i ) s.push_back(kChars[dist(rng)]); + return s; + } #ifdef _MSC_VER - const string FilePath::kSeparator = "\\"; - static const char kSeparatorChar = '\\'; - static const char kBackupSeparatorChar = '/'; - static const char kQuotedSeparatorChar = ':'; - static const char* kCurrentDir = ".\\"; - typedef struct _stat64 lc_stat_t; + const string FilePath::kSeparator = "\\"; + static const char kSeparatorChar = '\\'; + static const char kQuotedSeparatorChar = ':'; + static filesystem::path kCurrentDir = "."; + typedef struct _stat64 lc_stat_t; #else - const string FilePath::kSeparator = "/"; - static const char kSeparatorChar = '/'; - static const char kBackupSeparatorChar = '\\'; - static const char kQuotedSeparatorChar = ':'; - static const char* kCurrentDir = "./"; - typedef struct stat lc_stat_t; + const string FilePath::kSeparator = "/"; + static const char kSeparatorChar = '/'; + static const char kQuotedSeparatorChar = ':'; + static filesystem::path kCurrentDir = "."; + typedef struct stat lc_stat_t; #endif + FilePath::FilePath(std::filesystem::path&& path) : _path(std::move(path)) { + auto pathStr = _path.string(); - static string& appendSeparatorTo(string& str) { - if ( str.empty() || str[str.size() - 1] != kSeparatorChar ) str += kSeparatorChar; - return str; + // Trim off ending separator unless it's root because otherwise it + // messes with the existing parentDir() logic. + if ( pathStr.size() > 1 && pathStr.ends_with(kSeparator) ) { + _path = filesystem::path(pathStr.substr(0, pathStr.size() - 1)); + } } - FilePath::FilePath(string&& dirName, string&& fileName) : _dir(std::move(dirName)), _file(std::move(fileName)) { - if ( _dir.empty() ) _dir = kCurrentDir; - else if ( _dir[_dir.size() - 1] == kBackupSeparatorChar ) - _dir[_dir.size() - 1] = kSeparatorChar; - else - appendSeparatorTo(_dir); - } + FilePath::FilePath(const std::filesystem::path& path) : _path(path) { + auto pathStr = _path.string(); - FilePath::FilePath(string_view dir, string_view file) : FilePath(string(dir), string(file)) {} + // Trim off ending separator unless it's root because otherwise it + // messes with the existing parentDir() logic. + if ( pathStr.size() > 1 && pathStr.ends_with(kSeparator) ) { + _path = filesystem::path(pathStr.substr(0, pathStr.size() - 1)); + } + } - FilePath::FilePath(const char* dir, const char* file) : FilePath(string(dir), string(file)) {} + FilePath::FilePath() : FilePath(kCurrentDir) {} - FilePath::FilePath() : _dir(kCurrentDir), _file() {} + FilePath::operator alloc_slice() const { return alloc_slice(_path.string()); } pair FilePath::splitPath(string_view path) { - string dirname, basename; - auto slash = path.rfind(kSeparatorChar); - auto backupSlash = path.rfind(kBackupSeparatorChar); - if ( slash == string::npos && backupSlash == string::npos ) { return {kCurrentDir, string(path)}; } - - if ( slash == string::npos ) { - slash = backupSlash; - } else if ( backupSlash != string::npos ) { - slash = std::max(slash, backupSlash); - } + filesystem::path fsPath(path); + if ( !fsPath.has_root_path() ) { return {kCurrentDir.string(), string(path)}; } + + if ( filesystem::is_directory(fsPath) ) { return {fsPath.string(), ""}; } - return {string(path.substr(0, slash + 1)), string(path.substr(slash + 1))}; + return {fsPath.parent_path().string(), fsPath.filename().string()}; } pair FilePath::splitExtension(const string& file) { - auto dot = file.rfind('.'); - auto lastSlash = file.rfind(kSeparatorChar); - if ( dot == string::npos || (lastSlash != string::npos && dot < lastSlash) ) return {file, ""}; - else - return {file.substr(0, dot), file.substr(dot)}; + filesystem::path fsPath(file); + return {fsPath.stem().string(), fsPath.extension().string()}; } string FilePath::sanitizedFileName(string name) { @@ -192,28 +122,15 @@ namespace litecore { return name; } - string FilePath::unextendedName() const { return splitExtension(fileOrDirName()).first; } - - FilePath::operator alloc_slice() const { - auto dirSize = _dir.size(), fileSize = _file.size(); - alloc_slice result(dirSize + fileSize); - memcpy((void*)result.offset(0), _dir.data(), dirSize); - memcpy((void*)result.offset(dirSize), _file.data(), fileSize); - return result; - } - - string FilePath::extension() const { return splitExtension(fileOrDirName()).second; } - - static string addExtension(const string& name, const string& ext) { - return (ext[0] == '.') ? name + ext : name + "." + ext; + static filesystem::path addExtension(const filesystem::path& name, const string& ext) { + return (ext[0] == '.') ? name.filename().string() + ext : name.filename().string() + "." + ext; } FilePath FilePath::withExtension(const string& ext) const { - Assert(!isDir()); auto name = unextendedName(); - if ( ext.empty() ) return {_dir, name}; + if ( ext.empty() ) return filesystem::path(_path.parent_path()) / name; else - return {_dir, addExtension(name, ext)}; + return filesystem::path(_path.parent_path()) / addExtension(name, ext); } FilePath FilePath::withExtensionIfNone(const string& ext) const { @@ -223,63 +140,48 @@ namespace litecore { } FilePath FilePath::addingExtension(const string& ext) const { - Assert(!isDir()); if ( ext.empty() ) return *this; - else - return {_dir, addExtension(_file, ext)}; + return filesystem::path(_path.parent_path()) / addExtension(_path.filename(), ext); } FilePath FilePath::appendingToName(const std::string& suffix) const { - if ( isDir() ) - // Cut off the trailing slash, it will get added back in the constructor - return {_dir.substr(0, _dir.size() - 1) + suffix, _file}; - else - return {_dir, _file + suffix}; + auto otherPath = _path; + otherPath += suffix; + return FilePath(std::move(otherPath)); } FilePath FilePath::operator[](const string& name) const { - Assert(isDir()); - if ( name.empty() ) return *this; - else if ( name[name.size() - 1] == kSeparatorChar || name[name.size() - 1] == kBackupSeparatorChar ) - return {_dir + name, ""}; - else - return {_dir, name}; + assert_precondition(isDir()); + return FilePath(_path / name); } - string FilePath::fileOrDirName() const { - if ( !isDir() ) return fileName(); - // Remove the trailing separator from _dir: - auto path = _dir; - if ( path.size() <= 1 || path == kCurrentDir ) return ""; - chomp(path, kSeparatorChar); - chomp(path, kBackupSeparatorChar); - // Now return the last component: - auto split = splitPath(path); - return split.second; + FilePath FilePath::fileNamed(const std::string& filename) const { + assert_precondition(isDir()); + auto retVal = _path / filename; + if ( filesystem::exists(retVal) ) { assert_postcondition(!filesystem::is_directory(retVal)); } + return FilePath(std::move(retVal)); } - FilePath FilePath::fileNamed(const std::string& filename) const { return {_dir, filename}; } - - FilePath FilePath::subdirectoryNamed(const std::string& dirname) const { return {_dir + dirname, ""}; } + FilePath FilePath::subdirectoryNamed(const std::string& dirname) const { + assert_precondition(isDir()); + auto retVal = _path / dirname; + if ( filesystem::exists(retVal) ) { assert_postcondition(filesystem::is_directory(retVal)); } + return FilePath(std::move(retVal)); + } FilePath FilePath::parentDir() const { - if ( !isDir() ) return dir(); - - auto p = path(); + auto parent = _path.parent_path(); + if ( parent == _path ) { + return *this; // root directory + } + if ( parent.string().empty() ) { + if ( _path.string() == kCurrentDir ) { error::_throw(error::POSIX, EINVAL); } - if ( p == kCurrentDir ) error::_throw(error::POSIX, EINVAL); -#ifdef _MSC_VER - else if ( p.size() == 3 && p[1] == ':' && (p[2] == kSeparatorChar || p[2] == kBackupSeparatorChar) ) - return *this; -#else - else if ( p.size() == 1 && (p[0] == kSeparatorChar || p[0] == kBackupSeparatorChar) ) - return *this; -#endif + // relative path with no parent + return FilePath(kCurrentDir); + } - chomp(p, kSeparatorChar); - chomp(p, kBackupSeparatorChar); - auto parentDir = splitPath(p).first; - return {parentDir, ""}; + return FilePath(std::move(parent)); } /* static */ FilePath FilePath::sharedTempDirectory(const string& location) { @@ -292,206 +194,109 @@ namespace litecore { return alternate; } - string FilePath::canonicalPath() const { -#ifdef _MSC_VER - // Windows 10 has a new file path length limit of 32,767 chars (optionally) - const auto wcanon = (wchar_t*)malloc(sizeof(wchar_t) * 32768); - auto pathVal = path(); - const CA2WEX<256> wpath(pathVal.c_str(), CP_UTF8); - const DWORD copied = GetFullPathNameW(wpath, 32768, wcanon, nullptr); - wcanon[copied] = 0; - char* canon = nullptr; - if ( copied == 0 ) { - const DWORD err = GetLastError(); - if ( err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND ) { - _set_errno(ENOENT); - } else { - _set_errno(EBADF); - } - } else { - const CW2AEX<256> converted(wcanon, CP_UTF8); - - // Arbitrarily large to account for unforseen whacky UTF-8 names (4 bytes per wide char) - canon = (char*)malloc(32767 * 4 + 1); - strcpy_s(canon, 32767 * 4 + 1, converted.m_psz); - } - - free(wcanon); -#else - char* canon = ::realpath(path().c_str(), nullptr); -#endif - - if ( !canon ) { - if ( errno == ENOENT && !isDir() ) { - string canonDir = dir().canonicalPath(); - return appendSeparatorTo(canonDir) + _file; - } else { - error::_throwErrno(); - } - } - string canonStr(canon); - free(canon); - return canonStr; - } - #pragma mark - ENUMERATION: - static bool is_dir(const struct dirent* entry, const string& basePath) { - bool isDir = false; -#ifdef _DIRENT_HAVE_D_TYPE - if ( entry->d_type != DT_UNKNOWN && entry->d_type != DT_LNK ) { - isDir = (entry->d_type == DT_DIR); - } else -#endif - { - lc_stat_t stbuf; - stat_u8((basePath + entry->d_name).c_str(), &stbuf); - isDir = S_ISDIR(stbuf.st_mode); + void FilePath::forEachMatch(function_ref fn) const { + auto dir = isDir() ? _path : _path.parent_path(); + for ( const auto& entry : filesystem::directory_iterator(dir) ) { + if ( entry.path().filename() == "." || entry.path().filename() == ".." ) continue; + if ( !isDir() && entry.path().filename().string().find(_path.filename().string()) != 0 ) continue; + fn(FilePath(dir / entry)); } - - return isDir; } - void FilePath::forEachMatch(function_ref fn) const { - auto dir = opendir(_dir.c_str()); - if ( !dir ) error::_throwErrno(); - try { - while ( true ) { - struct dirent* result = readdir(dir); - if ( !result ) break; - string name(result->d_name); - if ( _file.empty() || name.find(_file) == 0 ) { - if ( is_dir(result, _dir) ) { - if ( name == "." || name == ".." ) continue; - fn(FilePath(_dir + name + '/', "")); - } else { - fn(FilePath(_dir, name)); - } - } - } - } catch ( ... ) { - closedir(dir); - throw; + void FilePath::forEachFile(function_ref fn) const { + auto dir = isDir() ? _path : _path.parent_path(); + for ( const auto& entry : filesystem::directory_iterator(dir) ) { + if ( entry.path().filename() == "." || entry.path().filename() == ".." ) continue; + fn(FilePath(dir / entry)); } - closedir(dir); } - void FilePath::forEachFile(function_ref fn) const { dir().forEachMatch(fn); } - #pragma mark - OPERATIONS: - static inline void check(int result) { - if ( _usuallyFalse(result != 0) ) error::_throwErrno(); + void FilePath::mustExistAsDir() const { + if ( !existsAsDir() ) error::_throw(error::POSIX, ENOTDIR); } - int64_t FilePath::dataSize() const { - lc_stat_t s; - if ( stat_u8(path().c_str(), &s) != 0 ) { - if ( errno == ENOENT ) return -1; - error::_throwErrno(); - } - return s.st_size; - } + bool FilePath::mkdir(int mode) const { + if ( filesystem::exists(_path) ) { return true; } - time_t FilePath::lastModified() const { - lc_stat_t s; - if ( stat_u8(path().c_str(), &s) != 0 ) { - if ( errno == ENOENT ) return -1; - error::_throwErrno(); - } - return s.st_mtime; - } + error_code ec; + if ( !filesystem::create_directory(_path, ec) ) { error::_throw(error::POSIX, to_errno(ec)); } - bool FilePath::exists() const noexcept { - lc_stat_t s; - return stat_u8(path().c_str(), &s) == 0; - } + filesystem::permissions(_path, perms_from_mode(mode), ec); + if ( ec ) { error::_throw(error::POSIX, to_errno(ec)); } - bool FilePath::existsAsDir() const noexcept { - lc_stat_t s; - return stat_u8(path().c_str(), &s) == 0 && S_ISDIR(s.st_mode); + return true; } - void FilePath::mustExistAsDir() const { - lc_stat_t s; - check(stat_u8(path().c_str(), &s)); - if ( !S_ISDIR(s.st_mode) ) error::_throw(error::POSIX, ENOTDIR); + static string makePathTemplate(const FilePath* fp) { + string path = fp->path(); + return path + randomSuffix(); } - bool FilePath::mkdir(int mode) const { - if ( mkdir_u8(path().c_str(), mode) != 0 ) { - if ( errno != EEXIST ) error::_throwErrno(); - return false; - } - return true; - } + FilePath FilePath::mkTempFile(FILE** outHandle) const { + for ( int i = 0; i < numeric_limits::max(); i++ ) { + auto pathTemplate = makePathTemplate(this); + if ( filesystem::exists(pathTemplate) ) { continue; } - static constexpr size_t kPathBufSize = 1024; // MAXPATHLEN + ofstream f(pathTemplate, ios::out | ios::binary | ios::trunc); + if ( !f ) { error(error::POSIX, EIO, "Unable to create temporary file")._throw(1); } - static void makePathTemplate(const FilePath* fp, char* pathBuf) { - string path = fp->path(); - const char* basePath = path.c_str(); - Assert(strlen(basePath) + 6 < kPathBufSize - 1); - snprintf(pathBuf, kPathBufSize, "%sXXXXXX", basePath); - } + if ( outHandle ) { + int fd = cbl_open(pathTemplate.c_str(), O_RDWR | O_TRUNC); + if ( fd < 0 ) { error(error::POSIX, errno, "Unable to open temporary file")._throw(1); } - FilePath FilePath::mkTempFile(FILE** outHandle) const { - char pathBuf[kPathBufSize]; - makePathTemplate(this, pathBuf); - int fd = mkstemp(pathBuf); - if ( fd < 0 ) error::_throwErrno(); - if ( outHandle ) { - *outHandle = fdopen(fd, "wb+"); - if ( *outHandle == nullptr ) { - fdclose(fd); - error::_throwErrno(); + *outHandle = fdopen(fd, "wb"); + if ( !*outHandle ) { + cbl_close(fd); + error(error::POSIX, errno, "Unable to fdopen temporary file")._throw(1); + } } - } else { - fdclose(fd); + + return FilePath(pathTemplate); } - return FilePath(pathBuf); + error::_throw(error::POSIX, EEXIST); } FilePath FilePath::mkTempDir() const { - char pathBuf[kPathBufSize]; - makePathTemplate(this, pathBuf); - if ( mkdtemp(pathBuf) == nullptr ) error::_throwErrno(); + for ( int i = 0; i < numeric_limits::max(); i++ ) { + auto pathTemplate = makePathTemplate(this); + if ( filesystem::exists(pathTemplate) ) { continue; } + + error_code ec; + if ( !filesystem::create_directory(pathTemplate, ec) ) { error::_throw(error::POSIX, to_errno(ec)); } - static const char separator[2] = {kSeparatorChar, (char)0}; - strlcat(pathBuf, separator, sizeof(pathBuf)); - return FilePath(pathBuf); + return FilePath(pathTemplate); + } + + error::_throw(error::POSIX, EEXIST); } bool FilePath::del() const { - std::string pathStr = path(); - const char* pathCStr = pathStr.c_str(); - - auto result = isDir() ? rmdir_u8(pathCStr) : unlink_u8(pathCStr); - if ( result == 0 ) return true; + error_code ec; + auto result = filesystem::remove(_path, ec); + if ( result ) { return true; } - if ( errno == ENOENT ) return false; + int err = to_errno(ec); + if ( err == 0 ) return false; #ifdef _MSC_VER if ( errno == EACCES ) { setReadOnly(false); - result = isDir() ? rmdir_u8(pathCStr) : unlink_u8(pathCStr); + result = filesystem::remove(_path, ec); if ( result == 0 ) { return true; } } #endif - error::_throwErrno("Couldn't delete file %s", pathCStr); + error(error::POSIX, to_errno(ec), "Couldn't delete file")._throw(1); } static void _delRecursive(const FilePath& path) { if ( path.isDir() ) { -#if 1 path.forEachFile([](const FilePath& f) { f.delRecursive(); }); -#else - vector children; - path.forEachFile([&](const FilePath& f) { children.push_back(f); }); - for ( auto child : children ) child.delRecursive(); -#endif } path.del(); } @@ -502,48 +307,17 @@ namespace litecore { return true; } - void FilePath::copyTo(const string& to) const { - std::string from = path(); - const char *fromPathStr = from.c_str(), *toPathStr = to.c_str(); - int result = 0; -#if __APPLE__ - copyfile_flags_t flags = COPYFILE_CLONE | COPYFILE_RECURSIVE; - result = copyfile(fromPathStr, toPathStr, nullptr, flags); -#else - if ( isDir() ) { - FilePath toPath(to); - toPath.mkdir(); - forEachFile([&toPath](const FilePath& f) { f.copyTo(toPath[f.fileOrDirName() + (f.isDir() ? "/" : "")]); }); - } else { - result = copyfile(fromPathStr, toPathStr); - } -#endif - if ( result != 0 ) { error::_throwErrno("Couldn't copy file from %s to %s", fromPathStr, toPathStr); } - } + void FilePath::copyTo(const string& to) const { filesystem::copy(_path, to, filesystem::copy_options::recursive); } void FilePath::moveTo(const string& to) const { - int result = rename_u8(path().c_str(), to.c_str()); -#ifdef _MSC_VER - // While unix will automatically overwrite the target, - // Windows will not - int loopCount = 0; - while ( result != 0 && errno == EEXIST ) { - if ( loopCount++ == 10 ) { - WarnError("Unable to move blob after 10 attempts, giving up..."); - error::_throw(error::POSIX, EEXIST); - } - - check(chmod_u8(to.c_str(), 0600)); - if ( (FilePath(to).isDir()) ) { - check(rmdir_u8(to.c_str())); - } else { - check(unlink_u8(to.c_str())); - } - - result = rename_u8(path().c_str(), to.c_str()); + error_code ec; + if ( filesystem::exists(to) ) { + filesystem::permissions(to, filesystem::perms::owner_write, filesystem::perm_options::add, ec); + if ( ec ) { error::_throw(error::POSIX, to_errno(ec)); } } -#endif - check(result); + + filesystem::rename(_path, to, ec); + if ( ec ) { error::_throw(error::POSIX, to_errno(ec)); } } void FilePath::moveToReplacingDir(const FilePath& to, bool asyncCleanup) const { @@ -561,7 +335,7 @@ namespace litecore { // Move the old item aside, to be deleted later: auto parent = to.parentDir().path(); FilePath trashDir(FilePath::sharedTempDirectory(parent)["CBL_Obsolete-"].mkTempDir()); - FilePath trashPath(std::string(trashDir), to.fileOrDirName()); + FilePath trashPath = trashDir[to.fileOrDirName()]; to.moveTo(trashPath); try { @@ -584,7 +358,13 @@ namespace litecore { } } - void FilePath::setReadOnly(bool readOnly) const { chmod_u8(path().c_str(), (readOnly ? 0400 : 0600)); } + void FilePath::setReadOnly(bool readOnly) const { + filesystem::perms p = readOnly ? filesystem::perms::owner_read + : (filesystem::perms::owner_read | filesystem::perms::owner_write); + error_code ec; + filesystem::permissions(_path, p, ec); + if ( ec ) { error::_throw(error::POSIX, to_errno(ec)); } + } } // namespace litecore diff --git a/LiteCore/Support/FilePath.hh b/LiteCore/Support/FilePath.hh index 699429e1a2..a979fd9024 100644 --- a/LiteCore/Support/FilePath.hh +++ b/LiteCore/Support/FilePath.hh @@ -18,6 +18,8 @@ #include #include #include +#include +#include "NumConversion.hh" namespace fleece { struct slice; @@ -32,14 +34,11 @@ namespace litecore { class FilePath { public: /** Constructs a FilePath from a filesystem path. */ - explicit FilePath(std::string_view path) { tie(_dir, _file) = splitPath(path); } + FilePath(std::filesystem::path&& path); - FilePath(); + FilePath(const std::filesystem::path& path); - /** Constructs a FilePath from a directory name and a filename in that directory. */ - FilePath(std::string&& dirName, std::string&& fileName); - FilePath(std::string_view dirName, std::string_view fileName); - FilePath(const char* dirName, const char* fileName); + FilePath(); /** Returns a folder with a predefined name that serves as a location for temporary files. @@ -50,24 +49,28 @@ namespace litecore { //////// DIRECTORY & FILE NAMES: - [[nodiscard]] bool isDir() const { return _file.empty(); } + [[nodiscard]] bool isDir() const { return std::filesystem::is_directory(_path); } - [[nodiscard]] FilePath dir() const { return {_dir, ""}; } + [[nodiscard]] FilePath dir() const { return isDir() ? *this : parentDir(); } - [[nodiscard]] const std::string& dirName() const { return _dir; } + [[nodiscard]] const std::string dirName() const { + return isDir() ? _path.filename().string() : _path.parent_path().filename().string(); + } - [[nodiscard]] const std::string& fileName() const { return _file; } + [[nodiscard]] const std::string fileName() const { return !isDir() ? _path.filename().string() : ""; } - [[nodiscard]] std::string fileOrDirName() const; + [[nodiscard]] std::string fileOrDirName() const { return _path.filename().string(); } - [[nodiscard]] std::string path() const { return _dir + _file; } + [[nodiscard]] std::string path() const { return _path.string(); } /** Returns a canonical standard form of the path by resolving symbolic links, normalizing capitalization (in case-insensitive filesystems), etc. */ - [[nodiscard]] std::string canonicalPath() const; + [[nodiscard]] std::string canonicalPath() const { return _path.lexically_normal().string(); } explicit operator std::string() const { return path(); } + operator std::filesystem::path() const { return _path; } + explicit operator fleece::alloc_slice() const; /** Converts a string to a valid filename by escaping invalid characters, @@ -76,8 +79,9 @@ namespace litecore { //////// FILENAME EXTENSIONS: - [[nodiscard]] std::string unextendedName() const; - [[nodiscard]] std::string extension() const; + [[nodiscard]] std::string unextendedName() const { return _path.stem().string(); } + + [[nodiscard]] std::string extension() const { return _path.extension().string(); } /** Adds a filename extension. `ext` may or may not start with '.'. Cannot be called on directories. */ @@ -109,15 +113,23 @@ namespace litecore { /////// FILESYSTEM OPERATIONS: - [[nodiscard]] bool exists() const noexcept; - [[nodiscard]] bool existsAsDir() const noexcept; - void mustExistAsDir() const; + [[nodiscard]] bool exists() const noexcept { return std::filesystem::exists(_path); } + + [[nodiscard]] bool existsAsDir() const noexcept { return std::filesystem::is_directory(_path); } + + void mustExistAsDir() const; /** Returns the size of the file in bytes, or -1 if the file does not exist. */ - [[nodiscard]] int64_t dataSize() const; + [[nodiscard]] int64_t dataSize() const { + return exists() ? fleece::narrow_cast(std::filesystem::file_size(_path)) : -1; + } /** Returns the date at which this file was last modified, or -1 if the file does not exist */ - [[nodiscard]] time_t lastModified() const; + [[nodiscard]] time_t lastModified() const { + return exists() ? fleece::narrow_cast( + std::filesystem::last_write_time(_path).time_since_epoch().count()) + : -1; + } /** * The return values of mkdir(), del(), and delRecursive() are almost never used throughout our code, @@ -176,8 +188,7 @@ namespace litecore { static std::pair splitExtension(const std::string& filename); private: - std::string _dir; // Directory; always non-empty, always ends with separator ('/') - std::string _file; // Filename, or empty if this represents a directory + std::filesystem::path _path; }; } // namespace litecore diff --git a/LiteCore/Support/PlatformIO.hh b/LiteCore/Support/PlatformIO.hh index edf2c7e84d..4d17bbef68 100644 --- a/LiteCore/Support/PlatformIO.hh +++ b/LiteCore/Support/PlatformIO.hh @@ -24,6 +24,8 @@ # define strncasecmp _strnicmp # define strcasecmp _stricmp # define fdclose ::_close +# define cbl_open ::_open +# define cbl_close ::_close # define R_OK 1 # define W_OK 2 @@ -48,7 +50,9 @@ namespace litecore { # include # include -# define fdclose ::close +# define fdclose ::close +# define cbl_open ::open +# define cbl_close ::close namespace litecore { inline int mkdir_u8(const char* const path NONNULL, int mode) { return ::mkdir(path, (mode_t)mode); } diff --git a/LiteCore/Support/TestsCommon.cc b/LiteCore/Support/TestsCommon.cc index f1546c7e59..8872a60590 100644 --- a/LiteCore/Support/TestsCommon.cc +++ b/LiteCore/Support/TestsCommon.cc @@ -22,6 +22,7 @@ #include #include #include +#include #ifdef _MSC_VER # include @@ -34,20 +35,6 @@ using namespace std; using namespace litecore; using namespace fleece; -FilePath GetSystemTempDirectory() { -#ifdef _MSC_VER - WCHAR pathBuffer[MAX_PATH + 1]; - GetTempPathW(MAX_PATH, pathBuffer); - GetLongPathNameW(pathBuffer, pathBuffer, MAX_PATH); - CW2AEX<256> convertedPath(pathBuffer, CP_UTF8); - return litecore::FilePath(convertedPath.m_psz, ""); -#else // _MSC_VER - const char* tmp = getenv("TMPDIR"); - if ( !tmp ) tmp = "/tmp"; - return {tmp, ""}; -#endif // _MSC_VER -} - FilePath GetTempDirectory() { static FilePath kTempDir; static once_flag f; @@ -56,7 +43,7 @@ FilePath GetTempDirectory() { char folderName[bufSize]; snprintf(folderName, bufSize, "LiteCore_Tests_%" PRIms ".cblite2/", chrono::milliseconds(time(nullptr)).count()); - kTempDir = GetSystemTempDirectory()[folderName]; + kTempDir = filesystem::temp_directory_path() / folderName; (void)kTempDir.mkdir(); // it's OK if it already exists }); diff --git a/LiteCore/Unix/strlcat.c b/LiteCore/Unix/strlcat.c deleted file mode 100644 index f8aa0c1fe3..0000000000 --- a/LiteCore/Unix/strlcat.c +++ /dev/null @@ -1,52 +0,0 @@ -/* $OpenBSD: strlcat.c,v 1.16 2015/08/31 02:53:57 guenther Exp $ */ - -/* -* Copyright (c) 1998, 2015 Todd C. Miller -* -* Permission to use, copy, modify, and distribute this software for any -* purpose with or without fee is hereby granted, provided that the above -* copyright notice and this permission notice appear in all copies. -* -* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#include "strlcat.h" -#include -#include - -/* -* Appends src to string dst of size dsize (unlike strncat, dsize is the -* full size of dst, not space left). At most dsize-1 characters -* will be copied. Always NUL terminates (unless dsize <= strlen(dst)). -* Returns strlen(src) + MIN(dsize, strlen(initial dst)). -* If retval >= dsize, truncation occurred. -*/ -size_t strlcat(char* dst, const char* src, size_t dsize) { - const char* odst = dst; - const char* osrc = src; - size_t n = dsize; - size_t dlen; - - /* Find the end of dst and adjust bytes left but don't go past end. */ - while ( n-- != 0 && *dst != '\0' ) dst++; - dlen = dst - odst; - n = dsize - dlen; - - if ( n-- == 0 ) return (dlen + strlen(src)); - while ( *src != '\0' ) { - if ( n != 0 ) { - *dst++ = *src; - n--; - } - src++; - } - *dst = '\0'; - - return (dlen + (src - osrc)); /* count does not include NUL */ -} \ No newline at end of file diff --git a/LiteCore/Unix/strlcat.h b/LiteCore/Unix/strlcat.h deleted file mode 100644 index d136732d74..0000000000 --- a/LiteCore/Unix/strlcat.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (c) 2017 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -// Adapted from OpenBSD string.h (Copyright (c) 1990 The Regents of the University of California.) -// Originally licensed under the BSD 3-clause license -// https://github.com/openbsd/src/blob/1693b10bbdb876791a8ba7277dd26d2fd5e7aff8/include/string.h#L127-L128 - -#pragma once - -#ifndef __cplusplus -# include -#else -# include -extern "C" { -#endif -size_t strlcat(char* dst, const char* src, size_t dsize); - -#ifdef __cplusplus -} -#endif diff --git a/LiteCore/tests/DataFileTest.cc b/LiteCore/tests/DataFileTest.cc index bfa3ad508b..6cb2bfd3c6 100644 --- a/LiteCore/tests/DataFileTest.cc +++ b/LiteCore/tests/DataFileTest.cc @@ -648,32 +648,26 @@ TEST_CASE_METHOD(DataFileTestFixture, "DataFile Compact", "[DataFile]") { TEST_CASE("CanonicalPath") { #ifdef _MSC_VER const char* startPath = "C:\\folder\\..\\subfolder\\"; - string endPath = "C:\\subfolder\\"; + string endPath = "C:\\subfolder"; #else auto tmpPath = TestFixture::sTempDir.path(); - auto startPath = tmpPath + "folder/"; + auto startPath = tmpPath + "/folder/"; ::mkdir(startPath.c_str(), 777); startPath += "../subfolder/"; - auto endPath = tmpPath + "subfolder"; + auto endPath = tmpPath + "/subfolder"; ::mkdir(endPath.c_str(), 777); -# if __APPLE__ && !TARGET_OS_IPHONE - endPath = "/private" + endPath; -# endif #endif FilePath path(startPath); CHECK(path.canonicalPath() == endPath); #ifdef _MSC_VER - startPath = (const char*)u8"C:\\日本語\\"; + startPath = (const char*)u8"C:\\日本語"; endPath = startPath; #else startPath = tmpPath + (const char*)u8"日本語"; ::mkdir(startPath.c_str(), 777); endPath = startPath; -# if __APPLE__ && !TARGET_OS_IPHONE - endPath = "/private" + endPath; -# endif #endif path = FilePath(startPath); @@ -682,23 +676,23 @@ TEST_CASE("CanonicalPath") { TEST_CASE("ParentDir") { #ifdef _MSC_VER - CHECK(FilePath("C:\\").parentDir().path() == "C:\\"); - CHECK(FilePath("D:\\folder\\subfolder\\file").parentDir().path() == "D:\\folder\\subfolder\\"); - CHECK(FilePath("C:\\folder\\subfolder\\").parentDir().path() == "C:\\folder\\"); - CHECK(FilePath("C:\\folder\\file").parentDir().path() == "C:\\folder\\"); + CHECK(FilePath("C:\\").parentDir().path() == "C:"); + CHECK(FilePath("D:\\folder\\subfolder\\file").parentDir().path() == "D:\\folder\\subfolder"); + CHECK(FilePath("C:\\folder\\subfolder\\").parentDir().path() == "C:\\folder"); + CHECK(FilePath("C:\\folder\\file").parentDir().path() == "C:\\folder"); #else CHECK(FilePath("/").parentDir().path() == "/"); - CHECK(FilePath("/folder/subfolder/file").parentDir().path() == "/folder/subfolder/"); - CHECK(FilePath("/folder/subfolder/").parentDir().path() == "/folder/"); - CHECK(FilePath("/folder/file").parentDir().path() == "/folder/"); + CHECK(FilePath("/folder/subfolder/file").parentDir().path() == "/folder/subfolder"); + CHECK(FilePath("/folder/subfolder/").parentDir().path() == "/folder"); + CHECK(FilePath("/folder/file").parentDir().path() == "/folder"); #endif - check_parent("folder/subfolder/", "folder/"); - check_parent("folder/file", "folder/"); - check_parent("./file", "./"); - check_parent("./folder/", "./"); - check_parent("file", "./"); - check_parent("folder/", "./"); + check_parent("folder/subfolder", "folder"); + check_parent("folder/file", "folder"); + check_parent("./file", "."); + check_parent("./folder/", "."); + check_parent("file", "."); + check_parent("folder/", "."); ExpectException(error::POSIX, EINVAL, [&] { stringstream ss; ss << "." << FilePath::kSeparator; diff --git a/MSVC/dirent.h b/MSVC/dirent.h deleted file mode 100644 index 9ee2737486..0000000000 --- a/MSVC/dirent.h +++ /dev/null @@ -1,835 +0,0 @@ -/* -* Dirent interface for Microsoft Visual Studio -* Version 1.21 -* -* Copyright (C) 2006-2012 Toni Ronkko -* This file is part of dirent. Dirent may be freely distributed -* under the MIT license. For all details and documentation, see -* https://github.com/tronkko/dirent -*/ -#ifndef DIRENT_H -#define DIRENT_H - -/* -* Include windows.h without Windows Sockets 1.1 to prevent conflicts with -* Windows Sockets 2.0. -*/ -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Indicates that d_type field is available in dirent structure */ -#define _DIRENT_HAVE_D_TYPE - -/* Indicates that d_namlen field is available in dirent structure */ -#define _DIRENT_HAVE_D_NAMLEN - -/* Entries missing from MSVC 6.0 */ -#if !defined(FILE_ATTRIBUTE_DEVICE) -# define FILE_ATTRIBUTE_DEVICE 0x40 -#endif - -/* File type and permission flags for stat(), general mask */ -#if !defined(S_IFMT) -# define S_IFMT _S_IFMT -#endif - -/* Directory bit */ -#if !defined(S_IFDIR) -# define S_IFDIR _S_IFDIR -#endif - -/* Character device bit */ -#if !defined(S_IFCHR) -# define S_IFCHR _S_IFCHR -#endif - -/* Pipe bit */ -#if !defined(S_IFFIFO) -# define S_IFFIFO _S_IFFIFO -#endif - -/* Regular file bit */ -#if !defined(S_IFREG) -# define S_IFREG _S_IFREG -#endif - -/* Read permission */ -#if !defined(S_IREAD) -# define S_IREAD _S_IREAD -#endif - -/* Write permission */ -#if !defined(S_IWRITE) -# define S_IWRITE _S_IWRITE -#endif - -/* Execute permission */ -#if !defined(S_IEXEC) -# define S_IEXEC _S_IEXEC -#endif - -/* Pipe */ -#if !defined(S_IFIFO) -# define S_IFIFO _S_IFIFO -#endif - -/* Block device */ -#if !defined(S_IFBLK) -# define S_IFBLK 0 -#endif - -/* Link */ -#if !defined(S_IFLNK) -# define S_IFLNK 0 -#endif - -/* Socket */ -#if !defined(S_IFSOCK) -# define S_IFSOCK 0 -#endif - -/* Read user permission */ -#if !defined(S_IRUSR) -# define S_IRUSR S_IREAD -#endif - -/* Write user permission */ -#if !defined(S_IWUSR) -# define S_IWUSR S_IWRITE -#endif - -/* Execute user permission */ -#if !defined(S_IXUSR) -# define S_IXUSR 0 -#endif - -/* Read group permission */ -#if !defined(S_IRGRP) -# define S_IRGRP 0 -#endif - -/* Write group permission */ -#if !defined(S_IWGRP) -# define S_IWGRP 0 -#endif - -/* Execute group permission */ -#if !defined(S_IXGRP) -# define S_IXGRP 0 -#endif - -/* Read others permission */ -#if !defined(S_IROTH) -# define S_IROTH 0 -#endif - -/* Write others permission */ -#if !defined(S_IWOTH) -# define S_IWOTH 0 -#endif - -/* Execute others permission */ -#if !defined(S_IXOTH) -# define S_IXOTH 0 -#endif - -/* Maximum length of file name */ -#if !defined(PATH_MAX) -# define PATH_MAX MAX_PATH -#endif -#if !defined(FILENAME_MAX) -# define FILENAME_MAX MAX_PATH -#endif -#if !defined(NAME_MAX) -# define NAME_MAX FILENAME_MAX -#endif - -/* File type flags for d_type */ -#define DT_UNKNOWN 0 -#define DT_REG S_IFREG -#define DT_DIR S_IFDIR -#define DT_FIFO S_IFIFO -#define DT_SOCK S_IFSOCK -#define DT_CHR S_IFCHR -#define DT_BLK S_IFBLK -#define DT_LNK S_IFLNK - -/* Macros for converting between st_mode and d_type */ -#define IFTODT(mode) ((mode)&S_IFMT) -#define DTTOIF(type) (type) - -/* -* File type macros. Note that block devices, sockets and links cannot be -* distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are -* only defined for compatibility. These macros should always return false -* on Windows. -*/ -#if !defined(S_ISFIFO) -# define S_ISFIFO(mode) (((mode)&S_IFMT) == S_IFIFO) -#endif -#if !defined(S_ISDIR) -# define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) -#endif -#if !defined(S_ISREG) -# define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) -#endif -#if !defined(S_ISLNK) -# define S_ISLNK(mode) (((mode)&S_IFMT) == S_IFLNK) -#endif -#if !defined(S_ISSOCK) -# define S_ISSOCK(mode) (((mode)&S_IFMT) == S_IFSOCK) -#endif -#if !defined(S_ISCHR) -# define S_ISCHR(mode) (((mode)&S_IFMT) == S_IFCHR) -#endif -#if !defined(S_ISBLK) -# define S_ISBLK(mode) (((mode)&S_IFMT) == S_IFBLK) -#endif - -/* Return the exact length of d_namlen without zero terminator */ -#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) - -/* Return number of bytes needed to store d_namlen */ -#define _D_ALLOC_NAMLEN(p) (PATH_MAX) - - -#ifdef __cplusplus -extern "C" { -#endif - - -/* Wide-character version */ -struct _wdirent { - /* Always zero */ - long d_ino; - - /* Structure size */ - unsigned short d_reclen; - - /* Length of name without \0 */ - size_t d_namlen; - - /* File type */ - int d_type; - - /* File name */ - wchar_t d_name[PATH_MAX]; -}; -typedef struct _wdirent _wdirent; - -struct _WDIR { - /* Current directory entry */ - struct _wdirent ent; - - /* Private file data */ - WIN32_FIND_DATAW data; - - /* True if data is valid */ - int cached; - - /* Win32 search handle */ - HANDLE handle; - - /* Initial directory name */ - wchar_t* patt; -}; -typedef struct _WDIR _WDIR; - -static _WDIR* _wopendir(const wchar_t* dirname); -static struct _wdirent* _wreaddir(_WDIR* dirp); -static int _wclosedir(_WDIR* dirp); -static void _wrewinddir(_WDIR* dirp); - - -/* For compatibility with Symbian */ -#define wdirent _wdirent -#define WDIR _WDIR -#define wopendir _wopendir -#define wreaddir _wreaddir -#define wclosedir _wclosedir -#define wrewinddir _wrewinddir - -/* Multi-byte character versions */ -struct dirent { - /* Always zero */ - long d_ino; - - /* Structure size */ - unsigned short d_reclen; - - /* Length of name without \0 */ - size_t d_namlen; - - /* File type */ - int d_type; - - /* File name */ - char d_name[PATH_MAX]; -}; -typedef struct dirent dirent; - -struct DIR { - struct dirent ent; - struct _WDIR* wdirp; -}; -typedef struct DIR DIR; - -static DIR* opendir(const char* dirname); -static struct dirent* readdir(DIR* dirp); -static int closedir(DIR* dirp); -static void rewinddir(DIR* dirp); - - -/* Internal utility functions */ -static WIN32_FIND_DATAW* dirent_first(_WDIR* dirp); -static WIN32_FIND_DATAW* dirent_next(_WDIR* dirp); - -static int dirent_mbstowcs_s(size_t* pReturnValue, wchar_t* wcstr, size_t sizeInWords, const char* mbstr, size_t count); - -static int dirent_wcstombs_s(size_t* pReturnValue, char* mbstr, size_t sizeInBytes, const wchar_t* wcstr, size_t count); - -static void dirent_set_errno(int error); - -/* - * Open directory stream DIRNAME for read and return a pointer to the - * internal working area that is used to retrieve individual directory - * entries. - */ -static _WDIR* _wopendir(const wchar_t* dirname) { - _WDIR* dirp = NULL; - int error; - - /* Must have directory name */ - if ( dirname == NULL || dirname[0] == '\0' ) { - dirent_set_errno(ENOENT); - return NULL; - } - - /* Allocate new _WDIR structure */ - dirp = (_WDIR*)malloc(sizeof(struct _WDIR)); - if ( dirp != NULL ) { - DWORD n; - - /* Reset _WDIR structure */ - dirp->handle = INVALID_HANDLE_VALUE; - dirp->patt = NULL; - dirp->cached = 0; - - /* Compute the length of full path plus zero terminator - * - * Note that on WinRT there's no way to convert relative paths - * into absolute paths, so just assume its an absolute path. - */ -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) - n = wcslen(dirname); -#else - n = GetFullPathNameW(dirname, 0, NULL, NULL); -#endif - - /* Allocate room for absolute directory name and search pattern */ - dirp->patt = (wchar_t*)malloc(sizeof(wchar_t) * n + 16); - if ( dirp->patt ) { - /* - * Convert relative directory name to an absolute one. This - * allows rewinddir() to function correctly even when current - * working directory is changed between opendir() and rewinddir(). - * - * Note that on WinRT there's no way to convert relative paths - * into absolute paths, so just assume its an absolute path. - */ -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) - wcsncpy_s(dirp->patt, n + 1, dirname, n); -#else - n = GetFullPathNameW(dirname, n, dirp->patt, NULL); -#endif - if ( n > 0 ) { - wchar_t* p; - - /* Append search pattern \* to the directory name */ - p = dirp->patt + n; - if ( dirp->patt < p ) { - switch ( p[-1] ) { - case '\\': - case '/': - case ':': - /* Directory ends in path separator, e.g. c:\temp\ */ - /*NOP*/; - break; - - default: - /* Directory name doesn't end in path separator */ - *p++ = '\\'; - } - } - *p++ = '*'; - *p = '\0'; - - /* Open directory stream and retrieve the first entry */ - if ( dirent_first(dirp) ) { - /* Directory stream opened successfully */ - error = 0; - } else { - /* Cannot retrieve first entry */ - error = 1; - dirent_set_errno(ENOENT); - } - - } else { - /* Cannot retrieve full path name */ - dirent_set_errno(ENOENT); - error = 1; - } - - } else { - /* Cannot allocate memory for search pattern */ - error = 1; - } - - } else { - /* Cannot allocate _WDIR structure */ - error = 1; - } - - /* Clean up in case of error */ - if ( error && dirp ) { - _wclosedir(dirp); - dirp = NULL; - } - - return dirp; -} - -/* - * Read next directory entry. The directory entry is returned in dirent - * structure in the d_name field. Individual directory entries returned by - * this function include regular files, sub-directories, pseudo-directories - * "." and ".." as well as volume labels, hidden files and system files. - */ -static struct _wdirent* _wreaddir(_WDIR* dirp) { - WIN32_FIND_DATAW* datap; - struct _wdirent* entp; - - /* Read next directory entry */ - datap = dirent_next(dirp); - if ( datap ) { - size_t n; - DWORD attr; - - /* Pointer to directory entry to return */ - entp = &dirp->ent; - - /* - * Copy file name as wide-character string. If the file name is too - * long to fit in to the destination buffer, then truncate file name - * to PATH_MAX characters and zero-terminate the buffer. - */ - n = 0; - while ( n + 1 < PATH_MAX && datap->cFileName[n] != 0 ) { - entp->d_name[n] = datap->cFileName[n]; - n++; - } - dirp->ent.d_name[n] = 0; - - /* Length of file name excluding zero terminator */ - entp->d_namlen = n; - - /* File type */ - attr = datap->dwFileAttributes; - if ( (attr & FILE_ATTRIBUTE_DEVICE) != 0 ) { - entp->d_type = DT_CHR; - } else if ( (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 ) { - entp->d_type = DT_DIR; - } else { - entp->d_type = DT_REG; - } - - /* Reset dummy fields */ - entp->d_ino = 0; - entp->d_reclen = sizeof(struct _wdirent); - - } else { - /* Last directory entry read */ - entp = NULL; - } - - return entp; -} - -/* - * Close directory stream opened by opendir() function. This invalidates the - * DIR structure as well as any directory entry read previously by - * _wreaddir(). - */ -static int _wclosedir(_WDIR* dirp) { - int ok; - if ( dirp ) { - /* Release search handle */ - if ( dirp->handle != INVALID_HANDLE_VALUE ) { - FindClose(dirp->handle); - dirp->handle = INVALID_HANDLE_VALUE; - } - - /* Release search pattern */ - if ( dirp->patt ) { - free(dirp->patt); - dirp->patt = NULL; - } - - /* Release directory structure */ - free(dirp); - ok = /*success*/ 0; - - } else { - /* Invalid directory stream */ - dirent_set_errno(EBADF); - ok = /*failure*/ -1; - } - return ok; -} - -/* - * Rewind directory stream such that _wreaddir() returns the very first - * file name again. - */ -static void _wrewinddir(_WDIR* dirp) { - if ( dirp ) { - /* Release existing search handle */ - if ( dirp->handle != INVALID_HANDLE_VALUE ) { FindClose(dirp->handle); } - - /* Open new search handle */ - dirent_first(dirp); - } -} - -/* Get first directory entry (internal) */ -static WIN32_FIND_DATAW* dirent_first(_WDIR* dirp) { - WIN32_FIND_DATAW* datap; - - /* Open directory and retrieve the first entry */ - dirp->handle = FindFirstFileExW(dirp->patt, FindExInfoStandard, &dirp->data, FindExSearchNameMatch, NULL, 0); - if ( dirp->handle != INVALID_HANDLE_VALUE ) { - /* a directory entry is now waiting in memory */ - datap = &dirp->data; - dirp->cached = 1; - - } else { - /* Failed to re-open directory: no directory entry in memory */ - dirp->cached = 0; - datap = NULL; - } - return datap; -} - -/* Get next directory entry (internal) */ -static WIN32_FIND_DATAW* dirent_next(_WDIR* dirp) { - WIN32_FIND_DATAW* p; - - /* Get next directory entry */ - if ( dirp->cached != 0 ) { - /* A valid directory entry already in memory */ - p = &dirp->data; - dirp->cached = 0; - - } else if ( dirp->handle != INVALID_HANDLE_VALUE ) { - /* Get the next directory entry from stream */ - if ( FindNextFileW(dirp->handle, &dirp->data) != FALSE ) { - /* Got a file */ - p = &dirp->data; - } else { - /* The very last entry has been processed or an error occured */ - FindClose(dirp->handle); - dirp->handle = INVALID_HANDLE_VALUE; - p = NULL; - } - - } else { - /* End of directory stream reached */ - p = NULL; - } - - return p; -} - -/* - * Open directory stream using plain old C-string. - */ -static DIR* opendir(const char* dirname) { - struct DIR* dirp; - int error; - - /* Must have directory name */ - if ( dirname == NULL || dirname[0] == '\0' ) { - dirent_set_errno(ENOENT); - return NULL; - } - - /* Allocate memory for DIR structure */ - dirp = (DIR*)malloc(sizeof(struct DIR)); - if ( dirp ) { - wchar_t wname[PATH_MAX]; - - /* Convert directory name to wide-character string */ - int rc = MultiByteToWideChar(CP_UTF8, 0, dirname, (int)strnlen_s(dirname, PATH_MAX) + 1, wname, PATH_MAX); - if ( rc ) { - /* Open directory stream using wide-character name */ - dirp->wdirp = _wopendir(wname); - if ( dirp->wdirp ) { - /* Directory stream opened */ - error = 0; - } else { - /* Failed to open directory stream */ - error = 1; - } - - } else { - /* - * Cannot convert file name to wide-character string. This - * occurs if the string contains invalid multi-byte sequences or - * the output buffer is too small to contain the resulting - * string. - */ - error = 1; - } - - } else { - /* Cannot allocate DIR structure */ - error = 1; - } - - /* Clean up in case of error */ - if ( error && dirp ) { - free(dirp); - dirp = NULL; - } - - return dirp; -} - -/* - * Read next directory entry. - * - * When working with text consoles, please note that file names returned by - * readdir() are represented in the default ANSI code page while any output to - * console is typically formatted on another code page. Thus, non-ASCII - * characters in file names will not usually display correctly on console. The - * problem can be fixed in two ways: (1) change the character set of console - * to 1252 using chcp utility and use Lucida Console font, or (2) use - * _cprintf function when writing to console. The _cprinf() will re-encode - * ANSI strings to the console code page so many non-ASCII characters will - * display correcly. - */ -static struct dirent* readdir(DIR* dirp) { - WIN32_FIND_DATAW* datap; - struct dirent* entp; - - /* Read next directory entry */ - datap = dirent_next(dirp->wdirp); - if ( datap ) { - size_t n; - int error; - - /* Attempt to convert file name to multi-byte string */ - error = dirent_wcstombs_s(&n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX); - - /* - * If the file name cannot be represented by a multi-byte string, - * then attempt to use old 8+3 file name. This allows traditional - * Unix-code to access some file names despite of unicode - * characters, although file names may seem unfamiliar to the user. - * - * Be ware that the code below cannot come up with a short file - * name unless the file system provides one. At least - * VirtualBox shared folders fail to do this. - */ - if ( error && datap->cAlternateFileName[0] != '\0' ) { - error = dirent_wcstombs_s(&n, dirp->ent.d_name, PATH_MAX, datap->cAlternateFileName, PATH_MAX); - } - - if ( !error ) { - DWORD attr; - - /* Initialize directory entry for return */ - entp = &dirp->ent; - - /* Length of file name excluding zero terminator */ - entp->d_namlen = n - 1; - - /* File attributes */ - attr = datap->dwFileAttributes; - if ( (attr & FILE_ATTRIBUTE_DEVICE) != 0 ) { - entp->d_type = DT_CHR; - } else if ( (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 ) { - entp->d_type = DT_DIR; - } else { - entp->d_type = DT_REG; - } - - /* Reset dummy fields */ - entp->d_ino = 0; - entp->d_reclen = sizeof(struct dirent); - - } else { - /* - * Cannot convert file name to multi-byte string so construct - * an errornous directory entry and return that. Note that - * we cannot return NULL as that would stop the processing - * of directory entries completely. - */ - entp = &dirp->ent; - entp->d_name[0] = '?'; - entp->d_name[1] = '\0'; - entp->d_namlen = 1; - entp->d_type = DT_UNKNOWN; - entp->d_ino = 0; - entp->d_reclen = 0; - } - - } else { - /* No more directory entries */ - entp = NULL; - } - - return entp; -} - -/* - * Close directory stream. - */ -static int closedir(DIR* dirp) { - int ok; - if ( dirp ) { - /* Close wide-character directory stream */ - ok = _wclosedir(dirp->wdirp); - dirp->wdirp = NULL; - - /* Release multi-byte character version */ - free(dirp); - - } else { - /* Invalid directory stream */ - dirent_set_errno(EBADF); - ok = /*failure*/ -1; - } - return ok; -} - -/* - * Rewind directory stream to beginning. - */ -static void rewinddir(DIR* dirp) { - /* Rewind wide-character string directory stream */ - _wrewinddir(dirp->wdirp); -} - -/* Convert multi-byte string to wide character string */ -static int dirent_mbstowcs_s(size_t* pReturnValue, wchar_t* wcstr, size_t sizeInWords, const char* mbstr, - size_t count) { - int error; - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - - /* Microsoft Visual Studio 2005 or later */ - error = mbstowcs_s(pReturnValue, wcstr, sizeInWords, mbstr, count); - -#else - - /* Older Visual Studio or non-Microsoft compiler */ - size_t n; - - /* Convert to wide-character string (or count characters) */ - n = mbstowcs(wcstr, mbstr, sizeInWords); - if ( !wcstr || n < count ) { - /* Zero-terminate output buffer */ - if ( wcstr && sizeInWords ) { - if ( n >= sizeInWords ) { n = sizeInWords - 1; } - wcstr[n] = 0; - } - - /* Length of resuting multi-byte string WITH zero terminator */ - if ( pReturnValue ) { *pReturnValue = n + 1; } - - /* Success */ - error = 0; - - } else { - /* Could not convert string */ - error = 1; - } - -#endif - - return error; -} - -/* Convert wide-character string to multi-byte string */ -static int dirent_wcstombs_s(size_t* pReturnValue, char* mbstr, size_t sizeInBytes, /* max size of mbstr */ - const wchar_t* wcstr, size_t count) { - int error; - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - - /* Microsoft Visual Studio 2005 or later */ - error = wcstombs_s(pReturnValue, mbstr, sizeInBytes, wcstr, count); - -#else - - /* Older Visual Studio or non-Microsoft compiler */ - size_t n; - - /* Convert to multi-byte string (or count the number of bytes needed) */ - n = wcstombs(mbstr, wcstr, sizeInBytes); - if ( !mbstr || n < count ) { - /* Zero-terminate output buffer */ - if ( mbstr && sizeInBytes ) { - if ( n >= sizeInBytes ) { n = sizeInBytes - 1; } - mbstr[n] = '\0'; - } - - /* Length of resulting multi-bytes string WITH zero-terminator */ - if ( pReturnValue ) { *pReturnValue = n + 1; } - - /* Success */ - error = 0; - - } else { - /* Cannot convert string */ - error = 1; - } - -#endif - - return error; -} - -/* Set errno variable */ -static void dirent_set_errno(int error) { -#if defined(_MSC_VER) && _MSC_VER >= 1400 - - /* Microsoft Visual Studio 2005 and later */ - _set_errno(error); - -#else - - /* Non-Microsoft compiler or older Microsoft compiler */ - errno = error; - -#endif -} - - -#ifdef __cplusplus -} -#endif -#endif /*DIRENT_H*/ diff --git a/MSVC/mkdtemp.cc b/MSVC/mkdtemp.cc deleted file mode 100644 index 201a82006c..0000000000 --- a/MSVC/mkdtemp.cc +++ /dev/null @@ -1,52 +0,0 @@ -/* $Id: compat_mkdtemp.c,v 1.2 2015/10/06 18:32:19 schwarze Exp $ */ -/* -* Copyright (c) 2015 Ingo Schwarze -* -* Permission to use, copy, modify, and distribute this software for any -* purpose with or without fee is hereby granted, provided that the above -* copyright notice and this permission notice appear in all copies. -* -* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -* -* The algorithm of this function is inspired by OpenBSD mkdtemp(3) -* by Theo de Raadt and Todd Miller, but the code differs. -*/ - -#include -#include -#include -#include -#include -#include -#include - -char* mkdtemp(char* path) { - char * start, *cp; - unsigned int tries; - - start = strchr(path, '\0'); - while ( start > path && start[-1] == 'X' ) { start--; } - - for ( tries = INT_MAX; tries; tries-- ) { - if ( _mktemp(path) == NULL ) { - errno = EEXIST; - return NULL; - } - - CA2WEX<256> wpath(path, CP_UTF8); - if ( _wmkdir(wpath) == 0 ) { return path; } - - if ( errno != EEXIST ) { return NULL; } - - for ( cp = start; *cp != '\0'; cp++ ) *cp = 'X'; - } - - errno = EEXIST; - return NULL; -} \ No newline at end of file diff --git a/MSVC/mkdtemp.h b/MSVC/mkdtemp.h deleted file mode 100644 index f6bf0fd4e3..0000000000 --- a/MSVC/mkdtemp.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright 2017-Present Couchbase, Inc. -// -// Use of this software is governed by the Business Source License included -// in the file licenses/BSL-Couchbase.txt. As of the Change Date specified -// in that file, in accordance with the Business Source License, use of this -// software will be governed by the Apache License, Version 2.0, included in -// the file licenses/APL2.txt. -// - -#pragma once - -char* mkdtemp(char* tmpl); \ No newline at end of file diff --git a/MSVC/mkstemp.cc b/MSVC/mkstemp.cc deleted file mode 100644 index 55d55cfa50..0000000000 --- a/MSVC/mkstemp.cc +++ /dev/null @@ -1,62 +0,0 @@ -// -// mkstemp.cc -// -// Copyright 2018-Present Couchbase, Inc. -// -// Use of this software is governed by the Business Source License included -// in the file licenses/BSL-Couchbase.txt. As of the Change Date specified -// in that file, in accordance with the Business Source License, use of this -// software will be governed by the Apache License, Version 2.0, included in -// the file licenses/APL2.txt. -// - - -#include -#include -#include -#include -#include "TempArray.hh" -#include "SecureRandomize.hh" -#include -#ifdef HAVE_UNISTD_H -# include -#endif -#ifdef HAVE_FCNTL_H -# include -#endif - -#ifndef HAVE_MKSTEMP - -const char letter_choices[63] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - -static char* mktemp_internal(char* templ) { - char* start = strchr(templ, '\0'); - while ( *(--start) == 'X' ) { - const uint32_t r = litecore::RandomNumber(62); - Assert(r < 62); - *start = letter_choices[r]; - } - - return templ; -} - -int mkstemp(char* tmp) { - const size_t len = strnlen_s(tmp, MAX_PATH + 1) + 1; - TempArray(cp, char, len); - strcpy_s(cp, len, tmp); - - for ( int i = 0; i < INT32_MAX; i++ ) { - mktemp_internal(tmp); - const CA2WEX<256> wpath(tmp, CP_UTF8); - // O_EXCL means trying to open an existing file is an error - const int fd = _wopen(wpath, O_RDWR | O_CREAT | O_EXCL | O_BINARY, _S_IREAD | _S_IWRITE); - if ( fd >= 0 || errno != EEXIST ) { return fd; } - - strcpy_s(tmp, len, cp); - } - - errno = EEXIST; - return -1; -} - -#endif \ No newline at end of file diff --git a/MSVC/mkstemp.h b/MSVC/mkstemp.h deleted file mode 100644 index e93f7cca78..0000000000 --- a/MSVC/mkstemp.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright 2017-Present Couchbase, Inc. -// -// Use of this software is governed by the Business Source License included -// in the file licenses/BSL-Couchbase.txt. As of the Change Date specified -// in that file, in accordance with the Business Source License, use of this -// software will be governed by the Apache License, Version 2.0, included in -// the file licenses/APL2.txt. -// - -#pragma once - -int mkstemp(char* tmpl); \ No newline at end of file diff --git a/cmake/platform_linux.cmake b/cmake/platform_linux.cmake index c75c3c348a..61ec86bd40 100644 --- a/cmake/platform_linux.cmake +++ b/cmake/platform_linux.cmake @@ -48,7 +48,6 @@ function(set_litecore_source_linux) ${LINUX_SSS_RESULT} ${BASE_LITECORE_FILES} LiteCore/Storage/UnicodeCollator_ICU.cc - LiteCore/Unix/strlcat.c LiteCore/Support/StringUtil_icu.cc PARENT_SCOPE ) diff --git a/cmake/platform_win.cmake b/cmake/platform_win.cmake index ca5aa57ebc..61f1aee48e 100644 --- a/cmake/platform_win.cmake +++ b/cmake/platform_win.cmake @@ -16,9 +16,7 @@ function(set_litecore_source) ${WIN_SSS_RESULT} ${BASE_LITECORE_FILES} LiteCore/Storage/UnicodeCollator_winapi.cc - MSVC/mkstemp.cc - MSVC/mkdtemp.cc - MSVC/strlcat.c + MSVC/mbedThreading.cc LiteCore/Support/StringUtil_winapi.cc Crypto/PublicKey+Windows.cc