From 2b6106c41f3959d4deb8efc58c9055de0339959e Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Thu, 23 Feb 2017 01:06:11 +0300 Subject: Impliment basic VirtFsDir for virtual fs based on directories. Api similar to VirtFs. VirtFsDir unused for now. --- configure.ac | 1 + data/test/CMakeLists.txt | 2 +- data/test/Makefile.am | 2 +- data/test/dir2/CMakeLists.txt | 6 + data/test/dir2/Makefile.am | 9 + data/test/dir2/file1.txt | 1 + data/test/dir2/file2.txt | 1 + src/CMakeLists.txt | 8 + src/Makefile.am | 5 + src/fs/files.cpp | 44 ++- src/fs/files.h | 4 + src/fs/virtdirentry.cpp | 34 +++ src/fs/virtdirentry.h | 41 +++ src/fs/virtfileprivate.cpp | 16 +- src/fs/virtfileprivate.h | 3 + src/fs/virtfs_unittest.cc | 27 +- src/fs/virtfsdir.cpp | 564 ++++++++++++++++++++++++++++++++++ src/fs/virtfsdir.h | 79 +++++ src/fs/virtfsdir_unittest.cc | 685 ++++++++++++++++++++++++++++++++++++++++++ src/fs/virtfstools.cpp | 17 ++ src/fs/virtfstools.h | 7 + 21 files changed, 1542 insertions(+), 14 deletions(-) create mode 100644 data/test/dir2/CMakeLists.txt create mode 100644 data/test/dir2/Makefile.am create mode 100644 data/test/dir2/file1.txt create mode 100644 data/test/dir2/file2.txt create mode 100644 src/fs/virtdirentry.cpp create mode 100644 src/fs/virtdirentry.h create mode 100644 src/fs/virtfsdir.cpp create mode 100644 src/fs/virtfsdir.h create mode 100644 src/fs/virtfsdir_unittest.cc diff --git a/configure.ac b/configure.ac index c7c9135c8..d21537aa8 100755 --- a/configure.ac +++ b/configure.ac @@ -559,6 +559,7 @@ data/sfx/Makefile data/sfx/system/Makefile data/test/Makefile data/test/dir1/Makefile +data/test/dir2/Makefile data/themes/Makefile data/themes/blacknblack/Makefile data/themes/blackwood/Makefile diff --git a/data/test/CMakeLists.txt b/data/test/CMakeLists.txt index 11d0b5b61..2141da3cc 100644 --- a/data/test/CMakeLists.txt +++ b/data/test/CMakeLists.txt @@ -21,4 +21,4 @@ SET(FILES INSTALL(FILES ${FILES} DESTINATION ${DATA_DIR}/test) -ADD_SUBDIRECTORY(dir1) +ADD_SUBDIRECTORY(dir1 dir2) diff --git a/data/test/Makefile.am b/data/test/Makefile.am index 2f7b7f0dd..3d4fa7f8e 100644 --- a/data/test/Makefile.am +++ b/data/test/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = dir1 +SUBDIRS = dir1 dir2 testdir = $(pkgdatadir)/data/test diff --git a/data/test/dir2/CMakeLists.txt b/data/test/dir2/CMakeLists.txt new file mode 100644 index 000000000..60f5429ec --- /dev/null +++ b/data/test/dir2/CMakeLists.txt @@ -0,0 +1,6 @@ +SET(FILES + file1.txt + file2.txt + ) + +INSTALL(FILES ${FILES} DESTINATION ${DATA_DIR}/test/dir2) diff --git a/data/test/dir2/Makefile.am b/data/test/dir2/Makefile.am new file mode 100644 index 000000000..17934f3df --- /dev/null +++ b/data/test/dir2/Makefile.am @@ -0,0 +1,9 @@ +dir2dir = $(pkgdatadir)/data/test/dir2 + +dir2_DATA = \ + file1.txt \ + file2.txt + +EXTRA_DIST = \ + $(dir2_DATA) \ + CMakeLists.txt diff --git a/data/test/dir2/file1.txt b/data/test/dir2/file1.txt new file mode 100644 index 000000000..7d4116bf2 --- /dev/null +++ b/data/test/dir2/file1.txt @@ -0,0 +1 @@ +file1 test line 1 diff --git a/data/test/dir2/file2.txt b/data/test/dir2/file2.txt new file mode 100644 index 000000000..aff8265a1 --- /dev/null +++ b/data/test/dir2/file2.txt @@ -0,0 +1 @@ +file2 test line 1 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 53ce3bba1..b70bf269d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -887,6 +887,8 @@ SET(SRCS utils/perfomance.cpp utils/perfomance.h utils/debugmemoryobject.h + fs/virtfsdir.cpp + fs/virtfsdir.h fs/virtfsrwops.cpp fs/virtfsrwops.h fs/virtfstools.cpp @@ -910,6 +912,8 @@ SET(SRCS utils/stringvector.h utils/timer.cpp utils/timer.h + fs/virtdirentry.cpp + fs/virtdirentry.h fs/virtfile.cpp fs/virtfile.h fs/virtfileprivate.cpp @@ -1773,6 +1777,8 @@ SET(DYE_CMD_SRCS fs/paths.h utils/perfomance.cpp utils/perfomance.h + fs/virtfsdir.cpp + fs/virtfsdir.h fs/virtfsrwops.cpp fs/virtfsrwops.h fs/virtfstools.cpp @@ -1792,6 +1798,8 @@ SET(DYE_CMD_SRCS utils/stringutils.h utils/timer.cpp utils/timer.h + fs/virtdirentry.cpp + fs/virtdirentry.h fs/virtfile.cpp fs/virtfile.h fs/virtfileprivate.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 1f50fa699..6c9b56543 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -551,6 +551,8 @@ BASE_SRC += events/actionevent.h \ utils/perfomance.cpp \ utils/perfomance.h \ utils/debugmemoryobject.h \ + fs/virtfsdir.cpp \ + fs/virtfsdir.h \ fs/virtfsrwops.cpp \ fs/virtfsrwops.h \ fs/virtfstools.cpp \ @@ -576,6 +578,8 @@ BASE_SRC += events/actionevent.h \ utils/stringvector.h \ utils/timer.cpp \ utils/timer.h \ + fs/virtdirentry.cpp \ + fs/virtdirentry.h \ fs/virtfile.cpp \ fs/virtfile.h \ fs/virtfileprivate.cpp \ @@ -1920,6 +1924,7 @@ manaplustests_SOURCES = ${SRC} \ utils/dumplibs_unittest.cc \ utils/checkutils_unittest.cc \ fs/virtfs_unittest.cc \ + fs/virtfsdir_unittest.cc \ utils/xml_unittest.cc \ utils/timer_unittest.cc \ utils/xmlutils_unittest.cc \ diff --git a/src/fs/files.cpp b/src/fs/files.cpp index 516283fe6..abb0ee956 100644 --- a/src/fs/files.cpp +++ b/src/fs/files.cpp @@ -30,7 +30,10 @@ #include "fs/virtlist.h" #endif // defined(ANDROID) || defined(__native_client__) +#include "utils/stringutils.h" + #include +#include #include "debug.h" @@ -206,13 +209,8 @@ int Files::copyFile(const std::string &restrict srcName, bool Files::existsLocal(const std::string &path) { - bool flg(false); - std::fstream file; - file.open(path.c_str(), std::ios::in); - if (file.is_open()) - flg = true; - file.close(); - return flg; + struct stat statbuf; + return stat(path.c_str(), &statbuf) == 0; } bool Files::loadTextFileLocal(const std::string &fileName, @@ -266,3 +264,35 @@ void Files::deleteFilesInDirectory(std::string path) closedir(dir); } } + +void Files::enumFiles(StringVect &files, + std::string path, + const bool skipSymlinks) +{ + if (findLast(path, "/") == false) + path += "/"; + const struct dirent *next_file = nullptr; + DIR *const dir = opendir(path.c_str()); + + if (dir) + { + while ((next_file = readdir(dir))) + { + const std::string file = next_file->d_name; + if (file == "." || file == "..") + continue; + if (skipSymlinks == true) + { + struct stat statbuf; + if (lstat(path.c_str(), &statbuf) == 0 && + S_ISLNK(statbuf.st_mode) != 0) + { + continue; + } + } + files.push_back(file); + } + closedir(dir); + } +} + diff --git a/src/fs/files.h b/src/fs/files.h index b91f7e9ab..65b2f6325 100644 --- a/src/fs/files.h +++ b/src/fs/files.h @@ -63,6 +63,10 @@ namespace Files const std::string &restrict text); void deleteFilesInDirectory(std::string path); + + void enumFiles(StringVect &files, + std::string path, + const bool skipSymlinks); } // namespace Files #endif // UTILS_FILES_H diff --git a/src/fs/virtdirentry.cpp b/src/fs/virtdirentry.cpp new file mode 100644 index 000000000..693aba6a3 --- /dev/null +++ b/src/fs/virtdirentry.cpp @@ -0,0 +1,34 @@ +/* + * The ManaPlus Client + * Copyright (C) 2017 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "fs/virtdirentry.h" + +#include "debug.h" + +VirtDirEntry::VirtDirEntry(const std::string &userDir, + const std::string &rootDir) : + mUserDir(userDir), + mRootDir(rootDir) +{ +} + +VirtDirEntry::~VirtDirEntry() +{ +} diff --git a/src/fs/virtdirentry.h b/src/fs/virtdirentry.h new file mode 100644 index 000000000..b3d3faff6 --- /dev/null +++ b/src/fs/virtdirentry.h @@ -0,0 +1,41 @@ +/* + * The ManaPlus Client + * Copyright (C) 2017 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef UTILS_VIRTDIRENTRY_H +#define UTILS_VIRTDIRENTRY_H + +#include + +#include "localconsts.h" + +struct VirtDirEntry final +{ + VirtDirEntry(const std::string &userDir, + const std::string &rootDir); + + A_DELETE_COPY(VirtDirEntry) + + ~VirtDirEntry(); + + std::string mUserDir; + std::string mRootDir; +}; + +#endif // UTILS_VIRTDIRENTRY_H diff --git a/src/fs/virtfileprivate.cpp b/src/fs/virtfileprivate.cpp index 6bd5d3b9a..1dcc6116c 100644 --- a/src/fs/virtfileprivate.cpp +++ b/src/fs/virtfileprivate.cpp @@ -20,15 +20,25 @@ #include "fs/virtfileprivate.h" +#include + #include "debug.h" VirtFilePrivate::VirtFilePrivate() : - mFile(nullptr) + mFile(nullptr), + mFd(-1) +{ +} + +VirtFilePrivate::VirtFilePrivate(const int fd) : + mFile(nullptr), + mFd(fd) { } VirtFilePrivate::VirtFilePrivate(PHYSFS_file *const file) : - mFile(file) + mFile(file), + mFd(-1) { } @@ -39,4 +49,6 @@ VirtFilePrivate::~VirtFilePrivate() PHYSFS_close(mFile); mFile = nullptr; } + if (mFd != -1) + close(mFd); } diff --git a/src/fs/virtfileprivate.h b/src/fs/virtfileprivate.h index 5b280de78..62e510142 100644 --- a/src/fs/virtfileprivate.h +++ b/src/fs/virtfileprivate.h @@ -34,11 +34,14 @@ struct VirtFilePrivate final explicit VirtFilePrivate(PHYSFS_file *const file); + explicit VirtFilePrivate(const int fd); + A_DELETE_COPY(VirtFilePrivate) ~VirtFilePrivate(); PHYSFS_file *mFile; + int mFd; }; #endif // UTILS_VIRTFILEPRIVATE_H diff --git a/src/fs/virtfs_unittest.cc b/src/fs/virtfs_unittest.cc index 4f3a74a7b..b26381f08 100644 --- a/src/fs/virtfs_unittest.cc +++ b/src/fs/virtfs_unittest.cc @@ -106,7 +106,7 @@ static void removeTemp(StringVect &restrict list) } } -TEST_CASE("VirtFs enumerateFiles") +TEST_CASE("VirtFs enumerateFiles1") { logger = new Logger; @@ -115,8 +115,8 @@ TEST_CASE("VirtFs enumerateFiles") VirtList *list = nullptr; - const int cnt1 = VirtFs::exists("test/test2.txt") ? 23 : 22; - const int cnt2 = 23; + const int cnt1 = VirtFs::exists("test/test2.txt") ? 24 : 23; + const int cnt2 = 24; VirtFs::permitLinks(false); list = VirtFs::enumerateFiles("test"); @@ -142,6 +142,27 @@ TEST_CASE("VirtFs enumerateFiles") delete2(logger); } +TEST_CASE("VirtFs enumerateFiles2") +{ + logger = new Logger; + + VirtFs::addDirToSearchPath("data/test/dir1", + Append_false); + VirtFs::addDirToSearchPath("../data/test/dir1", + Append_false); + + VirtList *list = nullptr; + + list = VirtFs::enumerateFiles("/"); + const size_t sz = list->names.size(); + REQUIRE(list->names.size() == 5); + VirtFs::freeList(list); + + VirtFs::removeDirFromSearchPath("data/test/dir1"); + VirtFs::removeDirFromSearchPath("../data/test/dir1"); + delete2(logger); +} + TEST_CASE("VirtFs isDirectory") { logger = new Logger(); diff --git a/src/fs/virtfsdir.cpp b/src/fs/virtfsdir.cpp new file mode 100644 index 000000000..4ca4acafa --- /dev/null +++ b/src/fs/virtfsdir.cpp @@ -0,0 +1,564 @@ +/* + * The ManaPlus Client + * Copyright (C) 2013-2017 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "fs/virtfsdir.h" + +#include "fs/files.h" +#include "fs/mkdir.h" +#include "fs/paths.h" +#include "fs/virtdirentry.h" +#include "fs/virtfs.h" +#include "fs/virtfile.h" +#include "fs/virtfileprivate.h" +#include "fs/virtlist.h" + +#include "utils/checkutils.h" +#include "utils/stringutils.h" + +#include +#include +#include +#include + +#include +#include + +#ifdef ANDROID +#include "fs/paths.h" +#endif // ANDROID + +#include "debug.h" + +extern const char *dirSeparator; + +namespace +{ + std::vector mEntries; + std::string mWriteDir; + bool mPermitLinks = false; +} // namespace + +namespace VirtFsDir +{ + namespace + { + static VirtFile *openFile(const std::string &restrict filename, + const int mode) + { + if (checkPath(filename) == false) + { + reportAlways("VirtFsDir::openFile invalid path: %s", + filename.c_str()); + return nullptr; + } + VirtDirEntry *const entry = searchEntryByPath(filename); + if (entry == nullptr) + return nullptr; + + const std::string path = entry->mRootDir + filename; + const int fd = open(path.c_str(), + mode, + S_IRUSR | S_IWUSR); + if (fd == -1) + { + reportAlways("VirtFsDir::openFile file open error: %s", + filename.c_str()); + return nullptr; + } + VirtFile *restrict const file = new VirtFile; + file->mPrivate = new VirtFilePrivate(fd); + + return file; + } + } // namespace + + VirtDirEntry *searchEntryByRoot(const std::string &restrict root) + { + FOR_EACH (std::vector::const_iterator, it, mEntries) + { + if ((*it)->mRootDir == root) + return *it; + } + return nullptr; + } + + VirtDirEntry *searchEntryByPath(const std::string &restrict path) + { + FOR_EACH (std::vector::const_iterator, it, mEntries) + { + VirtDirEntry *const entry = *it; + if (Files::existsLocal(entry->mRootDir + path)) + return entry; + } + return nullptr; + } + + bool addToSearchPathSilent(const std::string &newDir, + const Append append, + const SkipError skipError) + { + if (skipError == SkipError_false && + Files::existsLocal(newDir) == false) + { + logger->log("VirtFsDir::addToSearchPath directory not exists: %s", + newDir.c_str()); + return false; + } + if (newDir.find(".zip") != std::string::npos) + { + reportAlways("Called VirtFsDir::addToSearchPath with zip archive"); + return false; + } + std::string rootDir = newDir; + if (findLast(rootDir, std::string(dirSeparator)) == false) + rootDir += dirSeparator; + VirtDirEntry *const entry = VirtFsDir::searchEntryByRoot(rootDir); + if (entry != nullptr) + { + reportAlways("VirtFsDir::addToSearchPath already exists: %s", + newDir.c_str()); + return false; + } + logger->log("Add virtual directory: " + newDir); + if (append == Append_true) + { + mEntries.push_back(new VirtDirEntry(newDir, + rootDir)); + } + else + { + mEntries.insert(mEntries.begin(), + new VirtDirEntry(newDir, + rootDir)); + } + return true; + } + + bool addToSearchPath(const std::string &newDir, + const Append append) + { + if (Files::existsLocal(newDir) == false) + { + reportAlways("VirtFsDir::addToSearchPath directory not exists: %s", + newDir.c_str()); + return false; + } + if (newDir.find(".zip") != std::string::npos) + { + reportAlways("Called VirtFsDir::addToSearchPath with zip archive"); + return false; + } + std::string rootDir = newDir; + if (findLast(rootDir, std::string(dirSeparator)) == false) + rootDir += dirSeparator; + VirtDirEntry *const entry = VirtFsDir::searchEntryByRoot(rootDir); + if (entry != nullptr) + { + reportAlways("VirtFsDir::addToSearchPath already exists: %s", + newDir.c_str()); + return false; + } + logger->log("Add virtual directory: " + newDir); + if (append == Append_true) + { + mEntries.push_back(new VirtDirEntry(newDir, + rootDir)); + } + else + { + mEntries.insert(mEntries.begin(), + new VirtDirEntry(newDir, + rootDir)); + } + return true; + } + + bool removeFromSearchPathSilent(std::string oldDir) + { + if (oldDir.find(".zip") != std::string::npos) + { + reportAlways("Called removeFromSearchPath with zip archive"); + return false; + } + if (findLast(oldDir, std::string(dirSeparator)) == false) + oldDir += dirSeparator; + FOR_EACH (std::vector::iterator, it, mEntries) + { + VirtDirEntry *const entry = *it; + if (entry->mRootDir == oldDir) + { + logger->log("Remove virtual directory: " + oldDir); + mEntries.erase(it); + delete entry; + return true; + } + } + + logger->log("VirtFsDir::removeFromSearchPath not exists: %s", + oldDir.c_str()); + return false; + } + + bool removeFromSearchPath(std::string oldDir) + { + if (oldDir.find(".zip") != std::string::npos) + { + reportAlways("Called removeFromSearchPath with zip archive"); + return false; + } + if (findLast(oldDir, std::string(dirSeparator)) == false) + oldDir += dirSeparator; + FOR_EACH (std::vector::iterator, it, mEntries) + { + VirtDirEntry *const entry = *it; + if (entry->mRootDir == oldDir) + { + logger->log("Remove virtual directory: " + oldDir); + mEntries.erase(it); + delete entry; + return true; + } + } + + reportAlways("VirtFsDir::removeFromSearchPath not exists: %s", + oldDir.c_str()); + return false; + } + + std::vector &getEntries() + { + return mEntries; + } + + void deinit() + { + mEntries.clear(); + } + + std::string getRealDir(const std::string &restrict filename) + { + if (checkPath(filename) == false) + { + reportAlways("VirtFsDir::exists invalid path: %s", + filename.c_str()); + return std::string(); + } + FOR_EACH (std::vector::iterator, it, mEntries) + { + VirtDirEntry *const entry = *it; + const std::string path = entry->mRootDir + filename; + if (Files::existsLocal(path)) + return entry->mUserDir; + } + return std::string(); + } + + bool exists(const std::string &restrict name) + { + if (checkPath(name) == false) + { + reportAlways("VirtFsDir::exists invalid path: %s", + name.c_str()); + return false; + } + FOR_EACH (std::vector::iterator, it, mEntries) + { + VirtDirEntry *const entry = *it; + if (Files::existsLocal(entry->mRootDir + name)) + return true; + + } + return false; + } + + VirtList *enumerateFiles(const std::string &restrict dirName) + { + VirtList *const list = new VirtList; + if (checkPath(dirName) == false) + { + reportAlways("VirtFsDir::enumerateFiles invalid path: %s", + dirName.c_str()); + return list; + } + StringVect &names = list->names; + FOR_EACH (std::vector::iterator, it, mEntries) + { + VirtDirEntry *const entry = *it; + StringVect files; + std::string path = entry->mRootDir + dirName; + if (findLast(path, std::string(dirSeparator)) == false) + path += dirSeparator; + const struct dirent *next_file = nullptr; + DIR *const dir = opendir(path.c_str()); + if (dir) + { + while ((next_file = readdir(dir))) + { + const std::string file = next_file->d_name; + if (file == "." || file == "..") + continue; + if (mPermitLinks == false) + { + struct stat statbuf; + if (lstat(path.c_str(), &statbuf) == 0 && + S_ISLNK(statbuf.st_mode) != 0) + { + continue; + } + } + bool found(false); + FOR_EACH (StringVectCIter, itn, names) + { + if (*itn == file) + { + found = true; + break; + } + } + if (found == false) + names.push_back(file); + } + closedir(dir); + } + } + return list; + } + + bool isDirectory(const std::string &restrict dirName) + { + if (checkPath(dirName) == false) + { + reportAlways("VirtFsDir::isDirectory invalid path: %s", + dirName.c_str()); + return false; + } + FOR_EACH (std::vector::iterator, it, mEntries) + { + VirtDirEntry *const entry = *it; + std::string path = entry->mRootDir + dirName; + if (findLast(path, std::string(dirSeparator)) == false) + path += dirSeparator; + + struct stat statbuf; + if (stat(path.c_str(), &statbuf) == 0 && + S_ISDIR(statbuf.st_mode) != 0) + { + return true; + } + } + return false; + } + + bool isSymbolicLink(const std::string &restrict name) + { + if (checkPath(name) == false) + { + reportAlways("VirtFsDir::isSymbolicLink invalid path: %s", + name.c_str()); + return false; + } + if (mPermitLinks == false) + return false; + + struct stat statbuf; + return lstat(name.c_str(), &statbuf) == 0 && + S_ISLNK(statbuf.st_mode) != 0; + } + + void freeList(VirtList *restrict const handle) + { + delete handle; + } + + VirtFile *openRead(const std::string &restrict filename) + { + return openFile(filename, O_RDONLY); + } + + VirtFile *openWrite(const std::string &restrict filename) + { + return openFile(filename, O_WRONLY | O_CREAT | O_TRUNC); + } + + VirtFile *openAppend(const std::string &restrict filename) + { + return openFile(filename, O_WRONLY | O_CREAT | O_APPEND); + } + + bool setWriteDir(const std::string &restrict newDir) + { + mWriteDir = newDir; + if (findLast(mWriteDir, std::string(dirSeparator)) == false) + mWriteDir += dirSeparator; + return true; + } + + bool mkdir(const std::string &restrict dirname) + { + if (mWriteDir.empty()) + { + reportAlways("VirtFsDir::mkdir write dir is empty"); + return false; + } + return mkdir_r((mWriteDir + dirname).c_str()) != -1; + } + + bool remove(const std::string &restrict filename) + { + if (mWriteDir.empty()) + { + reportAlways("VirtFsDir::remove write dir is empty"); + return false; + } + return ::remove((mWriteDir + filename).c_str()) != 0; + } + + void permitLinks(const bool val) + { + mPermitLinks = val; + } + + const char *getLastError() + { + return nullptr; + } + + int close(VirtFile *restrict const file) + { + if (file == nullptr) + return 0; + delete file; + return 1; + } + + int64_t read(VirtFile *restrict const file, + void *restrict const buffer, + const uint32_t objSize, + const uint32_t objCount) + { + if (file == nullptr) + return 0; + const int fd = file->mPrivate->mFd; + if (fd == -1) + { + reportAlways("VirtFsDir::read file not opened."); + return 0; + } + int max = objSize * objCount; + int cnt = ::read(fd, buffer, max); + if (cnt <= 0) + return cnt; + return cnt / objSize; + } + + int64_t write(VirtFile *restrict const file, + const void *restrict const buffer, + const uint32_t objSize, + const uint32_t objCount) + { + if (file == nullptr) + return 0; + const int fd = file->mPrivate->mFd; + if (fd == -1) + { + reportAlways("VirtFsDir::write file not opened."); + return 0; + } + int max = objSize * objCount; + int cnt = ::write(fd, buffer, max); + if (cnt <= 0) + return cnt; + return cnt / objSize; + } + + int64_t fileLength(VirtFile *restrict const file) + { + if (file == nullptr) + return -1; + const int fd = file->mPrivate->mFd; + if (fd == -1) + { + reportAlways("VirtFsDir::fileLength file not opened."); + return 0; + } + struct stat statbuf; + if (fstat(fd, &statbuf) == -1) + { + reportAlways("VirtFsDir::fileLength error."); + return -1; + } + return (int64_t)statbuf.st_size; + } + + int64_t tell(VirtFile *restrict const file) + { + if (file == nullptr) + return -1; + + const int fd = file->mPrivate->mFd; + if (fd == -1) + { + reportAlways("VirtFsDir::tell file not opened."); + return 0; + } + const int64_t pos = lseek(fd, 0, SEEK_CUR); + return pos; + } + + int seek(VirtFile *restrict const file, + const uint64_t pos) + { + if (file == nullptr) + return -1; + + const int fd = file->mPrivate->mFd; + if (fd == -1) + { + reportAlways("VirtFsDir::seek file not opened."); + return 0; + } + const int64_t res = lseek(fd, pos, SEEK_SET); + if (res == -1) + return 0; + return 1; + } + + int eof(VirtFile *restrict const file) + { + if (file == nullptr) + return -1; + + const int fd = file->mPrivate->mFd; + if (fd == -1) + { + reportAlways("VirtFsDir::eof file not opened."); + return 0; + } + const int64_t pos = lseek(fd, 0, SEEK_CUR); + struct stat statbuf; + if (fstat(fd, &statbuf) == -1) + { + reportAlways("VirtFsDir::fileLength error."); + return -1; + } + const int64_t len = (int64_t)statbuf.st_size; + return pos < 0 || len < 0 || pos >= len; + } +} // namespace VirtFs diff --git a/src/fs/virtfsdir.h b/src/fs/virtfsdir.h new file mode 100644 index 000000000..4fa1c20a5 --- /dev/null +++ b/src/fs/virtfsdir.h @@ -0,0 +1,79 @@ +/* + * The ManaPlus Client + * Copyright (C) 2013-2017 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef UTILS_VIRTFSDIR_H +#define UTILS_VIRTFSDIR_H + +#include "enums/simpletypes/append.h" +#include "enums/simpletypes/skiperror.h" + +#include "localconsts.h" + +#include +#include + +struct VirtDirEntry; +struct VirtFile; +struct VirtList; + +namespace VirtFsDir +{ + VirtDirEntry *searchEntryByRoot(const std::string &restrict root); + VirtDirEntry *searchEntryByPath(const std::string &restrict path); + bool addToSearchPath(const std::string &newDir, + const Append append); + bool addToSearchPathSilent(const std::string &newDir, + const Append append, + const SkipError skipError); + bool removeFromSearchPath(std::string oldDir); + bool removeFromSearchPathSilent(std::string oldDir); + void deinit(); + std::vector &getEntries(); + bool exists(const std::string &restrict name); + VirtList *enumerateFiles(const std::string &restrict dir) RETURNS_NONNULL; + bool isDirectory(const std::string &restrict dirName); + bool isSymbolicLink(const std::string &restrict name); + void freeList(VirtList *restrict const handle); + VirtFile *openRead(const std::string &restrict filename); + VirtFile *openWrite(const std::string &restrict filename); + VirtFile *openAppend(const std::string &restrict filename); + bool setWriteDir(const std::string &restrict newDir); + std::string getRealDir(const std::string &restrict filename); + bool mkdir(const std::string &restrict dirName); + bool remove(const std::string &restrict filename); + void permitLinks(const bool val); + const char *getLastError(); + int64_t read(VirtFile *restrict const handle, + void *restrict const buffer, + const uint32_t objSize, + const uint32_t objCount); + int64_t write(VirtFile *restrict const file, + const void *restrict const buffer, + const uint32_t objSize, + const uint32_t objCount); + int close(VirtFile *restrict const file); + int64_t fileLength(VirtFile *restrict const file); + int64_t tell(VirtFile *restrict const file); + int seek(VirtFile *restrict const file, + const uint64_t pos); + int eof(VirtFile *restrict const file); +} // namespace VirtFsDir + +#endif // UTILS_VIRTFSDIR_H diff --git a/src/fs/virtfsdir_unittest.cc b/src/fs/virtfsdir_unittest.cc new file mode 100644 index 000000000..742c1aa72 --- /dev/null +++ b/src/fs/virtfsdir_unittest.cc @@ -0,0 +1,685 @@ +/* + * The ManaPlus Client + * Copyright (C) 2016-2017 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "catch.hpp" + +#include "fs/virtdirentry.h" +#include "fs/virtfs.h" +#include "fs/virtfsdir.h" +#include "fs/virtfstools.h" +#include "fs/virtlist.h" + +#include "utils/checkutils.h" +#include "utils/delete2.h" + +#include "debug.h" + +TEST_CASE("VirtFsDir getEntries") +{ + REQUIRE(VirtFsDir::getEntries().empty()); + REQUIRE(VirtFsDir::searchEntryByRoot("test") == nullptr); +} + +TEST_CASE("VirtFsDir addToSearchPath") +{ + logger = new Logger(); + SECTION("simple 1") + { + REQUIRE(VirtFsDir::addToSearchPathSilent("dir1", + Append_false, + SkipError_true)); + REQUIRE(VirtFsDir::searchEntryByRoot("dir1/") != nullptr); + REQUIRE(VirtFsDir::searchEntryByRoot("test/") == nullptr); + REQUIRE(VirtFsDir::getEntries().size() == 1); + REQUIRE(VirtFsDir::getEntries()[0]->mRootDir == "dir1/"); + REQUIRE(VirtFsDir::getEntries()[0]->mUserDir == "dir1"); + } + + SECTION("simple 2") + { + REQUIRE(VirtFsDir::addToSearchPathSilent("dir1/", + Append_true, + SkipError_true)); + REQUIRE(VirtFsDir::searchEntryByRoot("dir1/") != nullptr); + REQUIRE(VirtFsDir::searchEntryByRoot("test/") == nullptr); + REQUIRE(VirtFsDir::getEntries().size() == 1); + REQUIRE(VirtFsDir::getEntries()[0]->mRootDir == "dir1/"); + REQUIRE(VirtFsDir::getEntries()[0]->mUserDir == "dir1/"); + } + + SECTION("simple 3") + { + REQUIRE(VirtFsDir::addToSearchPathSilent("dir1", + Append_false, + SkipError_true)); + REQUIRE(VirtFsDir::addToSearchPathSilent("dir2", + Append_false, + SkipError_true)); + REQUIRE(VirtFsDir::searchEntryByRoot("dir1/") != nullptr); + REQUIRE(VirtFsDir::searchEntryByRoot("dir2/") != nullptr); + REQUIRE(VirtFsDir::searchEntryByRoot("test/") == nullptr); + REQUIRE(VirtFsDir::getEntries().size() == 2); + REQUIRE(VirtFsDir::getEntries()[0]->mRootDir == "dir2/"); + REQUIRE(VirtFsDir::getEntries()[1]->mRootDir == "dir1/"); + REQUIRE(VirtFsDir::getEntries()[0]->mUserDir == "dir2"); + REQUIRE(VirtFsDir::getEntries()[1]->mUserDir == "dir1"); + } + + SECTION("simple 4") + { + REQUIRE(VirtFsDir::addToSearchPathSilent("dir1", + Append_true, + SkipError_true)); + REQUIRE(VirtFsDir::addToSearchPathSilent("dir2", + Append_true, + SkipError_true)); + REQUIRE(VirtFsDir::searchEntryByRoot("dir1/") != nullptr); + REQUIRE(VirtFsDir::searchEntryByRoot("dir2/") != nullptr); + REQUIRE(VirtFsDir::searchEntryByRoot("test/") == nullptr); + REQUIRE(VirtFsDir::getEntries().size() == 2); + REQUIRE(VirtFsDir::getEntries()[0]->mRootDir == "dir1/"); + REQUIRE(VirtFsDir::getEntries()[1]->mRootDir == "dir2/"); + REQUIRE(VirtFsDir::getEntries()[0]->mUserDir == "dir1"); + REQUIRE(VirtFsDir::getEntries()[1]->mUserDir == "dir2"); + } + + SECTION("simple 5") + { + REQUIRE(VirtFsDir::addToSearchPathSilent("dir1", + Append_true, + SkipError_true)); + REQUIRE(VirtFsDir::addToSearchPathSilent("dir2", + Append_true, + SkipError_true)); + REQUIRE(VirtFsDir::addToSearchPathSilent("dir3/test", + Append_true, + SkipError_true)); + REQUIRE(VirtFsDir::searchEntryByRoot("dir1/") != nullptr); + REQUIRE(VirtFsDir::searchEntryByRoot("dir2/") != nullptr); + REQUIRE(VirtFsDir::searchEntryByRoot("dir3/test/") != nullptr); + REQUIRE(VirtFsDir::searchEntryByRoot("test/") == nullptr); + REQUIRE(VirtFsDir::getEntries().size() == 3); + REQUIRE(VirtFsDir::getEntries()[0]->mRootDir == "dir1/"); + REQUIRE(VirtFsDir::getEntries()[0]->mUserDir == "dir1"); + REQUIRE(VirtFsDir::getEntries()[1]->mRootDir == "dir2/"); + REQUIRE(VirtFsDir::getEntries()[1]->mUserDir == "dir2"); + REQUIRE(VirtFsDir::getEntries()[2]->mRootDir == "dir3/test/"); + REQUIRE(VirtFsDir::getEntries()[2]->mUserDir == "dir3/test"); + } + + SECTION("simple 6") + { + REQUIRE(VirtFsDir::addToSearchPathSilent("dir1", + Append_true, + SkipError_true)); + REQUIRE(VirtFsDir::addToSearchPathSilent("dir2", + Append_true, + SkipError_true)); + REQUIRE(VirtFsDir::addToSearchPathSilent("dir3/test", + Append_false, + SkipError_true)); + REQUIRE(VirtFsDir::searchEntryByRoot("dir1/") != nullptr); + REQUIRE(VirtFsDir::searchEntryByRoot("dir2/") != nullptr); + REQUIRE(VirtFsDir::searchEntryByRoot("dir3/test/") != nullptr); + REQUIRE(VirtFsDir::searchEntryByRoot("test/") == nullptr); + REQUIRE(VirtFsDir::getEntries().size() == 3); + REQUIRE(VirtFsDir::getEntries()[0]->mRootDir == "dir3/test/"); + REQUIRE(VirtFsDir::getEntries()[0]->mUserDir == "dir3/test"); + REQUIRE(VirtFsDir::getEntries()[1]->mRootDir == "dir1/"); + REQUIRE(VirtFsDir::getEntries()[1]->mUserDir == "dir1"); + REQUIRE(VirtFsDir::getEntries()[2]->mRootDir == "dir2/"); + REQUIRE(VirtFsDir::getEntries()[2]->mUserDir == "dir2"); + } + + VirtFsDir::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsDir removeFromSearchPath") +{ + logger = new Logger(); + + SECTION("simple 1") + { + REQUIRE_THROWS(VirtFsDir::removeFromSearchPath("dir1")); + } + + SECTION("simple 2") + { + REQUIRE(VirtFsDir::addToSearchPathSilent("dir1", + Append_true, + SkipError_true)); + REQUIRE_THROWS(VirtFsDir::removeFromSearchPath("dir2")); + REQUIRE(VirtFsDir::removeFromSearchPath("dir1")); + } + + SECTION("simple 3") + { + REQUIRE(VirtFsDir::addToSearchPathSilent("dir1", + Append_true, + SkipError_true)); + REQUIRE(VirtFsDir::addToSearchPathSilent("dir2/dir3", + Append_true, + SkipError_true)); + REQUIRE(VirtFsDir::addToSearchPathSilent("dir3", + Append_false, + SkipError_true)); + REQUIRE(VirtFsDir::getEntries().size() == 3); + REQUIRE_THROWS(VirtFsDir::removeFromSearchPath("dir2")); + REQUIRE(VirtFsDir::removeFromSearchPath("dir1")); + REQUIRE(VirtFsDir::getEntries().size() == 2); + REQUIRE(VirtFsDir::getEntries()[0]->mRootDir == "dir3/"); + REQUIRE(VirtFsDir::getEntries()[0]->mUserDir == "dir3"); + REQUIRE(VirtFsDir::getEntries()[1]->mRootDir == "dir2/dir3/"); + REQUIRE(VirtFsDir::getEntries()[1]->mUserDir == "dir2/dir3"); + REQUIRE_THROWS(VirtFsDir::removeFromSearchPath("dir1")); + REQUIRE(VirtFsDir::getEntries().size() == 2); + REQUIRE(VirtFsDir::getEntries()[0]->mRootDir == "dir3/"); + REQUIRE(VirtFsDir::getEntries()[0]->mUserDir == "dir3"); + REQUIRE(VirtFsDir::getEntries()[1]->mRootDir == "dir2/dir3/"); + REQUIRE(VirtFsDir::getEntries()[1]->mUserDir == "dir2/dir3"); + REQUIRE(VirtFsDir::removeFromSearchPath("dir2/dir3")); + REQUIRE_THROWS(VirtFsDir::removeFromSearchPath("dir2/dir3/")); + REQUIRE(VirtFsDir::getEntries().size() == 1); + REQUIRE(VirtFsDir::getEntries()[0]->mRootDir == "dir3/"); + REQUIRE(VirtFsDir::getEntries()[0]->mUserDir == "dir3"); + } + + SECTION("simple 4") + { + REQUIRE(VirtFsDir::addToSearchPathSilent("dir1", + Append_true, + SkipError_true)); + REQUIRE(VirtFsDir::getEntries().size() == 1); + REQUIRE(VirtFsDir::getEntries()[0]->mRootDir == "dir1/"); + REQUIRE(VirtFsDir::getEntries()[0]->mUserDir == "dir1"); + REQUIRE_THROWS(VirtFsDir::removeFromSearchPath("dir2")); + REQUIRE(VirtFsDir::removeFromSearchPath("dir1")); + REQUIRE(VirtFsDir::getEntries().size() == 0); + REQUIRE(VirtFsDir::addToSearchPathSilent("dir1", + Append_true, + SkipError_true)); + REQUIRE(VirtFsDir::getEntries().size() == 1); + REQUIRE(VirtFsDir::getEntries()[0]->mRootDir == "dir1/"); + REQUIRE(VirtFsDir::getEntries()[0]->mUserDir == "dir1"); + } + + VirtFsDir::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsDir exists") +{ + logger = new Logger(); + VirtFsDir::addToSearchPathSilent("data", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data", + Append_false, + SkipError_false); + + REQUIRE(VirtFsDir::exists("test/units.xml") == true); + REQUIRE(VirtFsDir::exists("test/units123.xml") == false); + REQUIRE(VirtFsDir::exists("tesQ/units.xml") == false); + REQUIRE(VirtFsDir::exists("units.xml") == false); + + VirtFsDir::addToSearchPathSilent("data/test", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data/test", + Append_false, + SkipError_false); + + REQUIRE(VirtFsDir::exists("test/units.xml") == true); + REQUIRE(VirtFsDir::exists("test/units123.xml") == false); + REQUIRE(VirtFsDir::exists("tesQ/units.xml") == false); + REQUIRE(VirtFsDir::exists("units.xml") == true); + + VirtFsDir::removeFromSearchPathSilent("data/test"); + VirtFsDir::removeFromSearchPathSilent("../data/test"); + + REQUIRE(VirtFsDir::exists("test/units.xml") == true); + REQUIRE(VirtFsDir::exists("test/units123.xml") == false); + REQUIRE(VirtFsDir::exists("tesQ/units.xml") == false); + REQUIRE(VirtFsDir::exists("units.xml") == false); + + REQUIRE_THROWS(VirtFsDir::exists("test/../units.xml")); + + VirtFsDir::deinit(); + delete2(logger); +} + +static void removeTemp(StringVect &restrict list) +{ + int cnt = 0; + std::sort(list.begin(), list.end()); + + FOR_EACH (StringVectIter, it, list) + { + if (*it != "serverlistplus.xml.part") + { + logger->log("file: %d %s", + cnt, + (*it).c_str()); + cnt ++; + } + } + + FOR_EACH (StringVectIter, it, list) + { + if (*it == "serverlistplus.xml.part") + { + list.erase(it); + return; + } + } +} + +TEST_CASE("VirtFsDir getRealDir") +{ + logger = new Logger(); + REQUIRE(VirtFsDir::getRealDir(".") == ""); + REQUIRE(VirtFsDir::getRealDir("..") == ""); + const bool dir1 = VirtFsDir::addToSearchPathSilent("data", + Append_false, + SkipError_false); + REQUIRE((dir1 || VirtFsDir::addToSearchPathSilent("../data", + Append_false, + SkipError_false)) == true); + if (dir1 == true) + { + REQUIRE(VirtFsDir::getRealDir("test") == "data"); + REQUIRE(VirtFsDir::getRealDir("test/test.txt") == + "data"); + } + else + { + REQUIRE(VirtFsDir::getRealDir("test") == "../data"); + REQUIRE(VirtFsDir::getRealDir("test/test.txt") == + "../data"); + } + REQUIRE(VirtFsDir::getRealDir("zzz") == ""); + + VirtFsDir::addToSearchPathSilent("data/test", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data/test", + Append_false, + SkipError_false); + if (dir1 == true) + { + REQUIRE(VirtFsDir::getRealDir("test") == "data"); + REQUIRE(VirtFsDir::getRealDir("test/test.txt") == + "data"); + REQUIRE(VirtFsDir::getRealDir("test.txt") == + "data/test"); + } + else + { + REQUIRE(VirtFsDir::getRealDir("test") == "../data"); + REQUIRE(VirtFsDir::getRealDir("test/test.txt") == + "../data"); + REQUIRE(VirtFsDir::getRealDir("test.txt") == + "../data/test"); + } + REQUIRE(VirtFsDir::getRealDir("zzz") == ""); + + VirtFsDir::removeFromSearchPathSilent("data/test"); + VirtFsDir::removeFromSearchPathSilent("../data/test"); + + if (dir1 == true) + { + REQUIRE(VirtFsDir::getRealDir("test") == "data"); + REQUIRE(VirtFsDir::getRealDir("test/test.txt") == + "data"); + } + else + { + REQUIRE(VirtFsDir::getRealDir("test") == "../data"); + REQUIRE(VirtFsDir::getRealDir("test/test.txt") == + "../data"); + } + REQUIRE(VirtFsDir::getRealDir("zzz") == ""); + + VirtFsDir::removeFromSearchPathSilent("data"); + VirtFsDir::removeFromSearchPathSilent("../data"); + VirtFsDir::deinit(); + delete2(logger); +} + +static bool inList(VirtList *list, + const std::string &name) +{ + FOR_EACH (StringVectCIter, it, list->names) + { + if (*it == name) + return true; + } + return false; +} + +TEST_CASE("VirtFsDir enumerateFiles1") +{ + logger = new Logger; + + VirtFsDir::addToSearchPathSilent("data", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data", + Append_false, + SkipError_false); + + VirtList *list = nullptr; + + const int cnt1 = VirtFsDir::exists("test/test2.txt") ? 24 : 23; + const int cnt2 = 24; + + VirtFsDir::permitLinks(false); + list = VirtFsDir::enumerateFiles("test"); + removeTemp(list->names); + const size_t sz = list->names.size(); + REQUIRE(sz == cnt1); + VirtFsDir::freeList(list); + + VirtFsDir::permitLinks(true); + list = VirtFsDir::enumerateFiles("test"); + removeTemp(list->names); + REQUIRE(list->names.size() == cnt2); + VirtFsDir::freeList(list); + + VirtFsDir::permitLinks(false); + list = VirtFsDir::enumerateFiles("test"); + removeTemp(list->names); + REQUIRE(list->names.size() == cnt1); + VirtFsDir::freeList(list); + + VirtFsDir::removeFromSearchPathSilent("data"); + VirtFsDir::removeFromSearchPathSilent("../data"); + VirtFsDir::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsDir enumerateFiles2") +{ + logger = new Logger; + + VirtFsDir::addToSearchPathSilent("data/test/dir1", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data/test/dir1", + Append_false, + SkipError_false); + + VirtList *list = nullptr; + + list = VirtFsDir::enumerateFiles("/"); + const size_t sz = list->names.size(); + REQUIRE(list->names.size() == 5); + REQUIRE(inList(list, "file1.txt")); + REQUIRE_FALSE(inList(list, "file2.txt")); + VirtFsDir::freeList(list); + VirtFsDir::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsDir enumerateFiles3") +{ + logger = new Logger; + + VirtFsDir::addToSearchPathSilent("data/test/dir1", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data/test/dir1", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("data/test/dir2", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data/test/dir2", + Append_false, + SkipError_false); + + VirtList *list = nullptr; + + list = VirtFsDir::enumerateFiles("/"); + const size_t sz = list->names.size(); + REQUIRE(list->names.size() == 6); + REQUIRE(inList(list, "file1.txt")); + REQUIRE(inList(list, "file2.txt")); + VirtFsDir::freeList(list); + VirtFsDir::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsDir isDirectory") +{ + logger = new Logger(); + VirtFsDir::addToSearchPathSilent("data", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data", + Append_false, + SkipError_false); + + REQUIRE(VirtFsDir::isDirectory("test/units.xml") == false); + REQUIRE(VirtFsDir::isDirectory("test/units.xml/") == false); + REQUIRE(VirtFsDir::isDirectory("test//units.xml") == false); + REQUIRE(VirtFsDir::isDirectory("test/units123.xml") == false); + REQUIRE(VirtFsDir::isDirectory("test//units123.xml") == false); + REQUIRE(VirtFsDir::isDirectory("tesQ/units.xml") == false); + REQUIRE(VirtFsDir::isDirectory("tesQ//units.xml") == false); + REQUIRE(VirtFsDir::isDirectory("units.xml") == false); + REQUIRE(VirtFsDir::isDirectory("test") == true); + REQUIRE(VirtFsDir::isDirectory("test/") == true); + REQUIRE(VirtFsDir::isDirectory("test//") == true); + REQUIRE(VirtFsDir::isDirectory("test/dir1") == true); + REQUIRE(VirtFsDir::isDirectory("test//dir1") == true); + REQUIRE(VirtFsDir::isDirectory("test//dir1/") == true); + REQUIRE(VirtFsDir::isDirectory("test//dir1//") == true); + REQUIRE(VirtFsDir::isDirectory("test/dir1/") == true); + REQUIRE(VirtFsDir::isDirectory("test/dir1//") == true); + REQUIRE(VirtFsDir::isDirectory("testQ") == false); + REQUIRE(VirtFsDir::isDirectory("testQ/") == false); + REQUIRE(VirtFsDir::isDirectory("testQ//") == false); + + VirtFsDir::addToSearchPathSilent("data/test", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data/test", + Append_false, + SkipError_false); + + REQUIRE(VirtFsDir::isDirectory("test/units.xml") == false); + REQUIRE(VirtFsDir::isDirectory("test/units.xml/") == false); + REQUIRE(VirtFsDir::isDirectory("test//units.xml") == false); + REQUIRE(VirtFsDir::isDirectory("test/units123.xml") == false); + REQUIRE(VirtFsDir::isDirectory("tesQ/units.xml") == false); + REQUIRE(VirtFsDir::isDirectory("units.xml") == false); + REQUIRE(VirtFsDir::isDirectory("test") == true); + REQUIRE(VirtFsDir::isDirectory("testQ") == false); + REQUIRE(VirtFsDir::isDirectory("test/dir1") == true); + + VirtFsDir::removeFromSearchPathSilent("data/test"); + VirtFsDir::removeFromSearchPathSilent("../data/test"); + + REQUIRE(VirtFsDir::isDirectory("test/units.xml") == false); + REQUIRE(VirtFsDir::isDirectory("test/units123.xml") == false); + REQUIRE(VirtFsDir::isDirectory("tesQ/units.xml") == false); + REQUIRE(VirtFsDir::isDirectory("units.xml") == false); + REQUIRE(VirtFsDir::isDirectory("units.xml/") == false); + REQUIRE(VirtFsDir::isDirectory("test") == true); + REQUIRE(VirtFsDir::isDirectory("test/") == true); + REQUIRE(VirtFsDir::isDirectory("testQ") == false); + REQUIRE(VirtFsDir::isDirectory("test/dir1") == true); + + VirtFsDir::removeFromSearchPathSilent("data"); + VirtFsDir::removeFromSearchPathSilent("../data"); + VirtFsDir::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsDir openRead") +{ + logger = new Logger(); + VirtFsDir::addToSearchPathSilent("data", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data", + Append_false, + SkipError_false); + + VirtFile *file = nullptr; + + file = VirtFsDir::openRead("test/units.xml"); + REQUIRE(file != nullptr); + VirtFsDir::close(file); + file = VirtFsDir::openRead("test/units123.xml"); + REQUIRE(file == nullptr); + file = VirtFsDir::openRead("tesQ/units.xml"); + REQUIRE(file == nullptr); + file = VirtFsDir::openRead("units.xml"); + REQUIRE(file == nullptr); +// file = VirtFsDir::openRead("test"); +// REQUIRE(file == nullptr); + file = VirtFsDir::openRead("testQ"); + REQUIRE(file == nullptr); + + VirtFsDir::addToSearchPathSilent("data/test", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data/test", + Append_false, + SkipError_false); + + file = VirtFsDir::openRead("test/units.xml"); + REQUIRE(file != nullptr); + VirtFsDir::close(file); + file = VirtFsDir::openRead("test/units123.xml"); + REQUIRE(file == nullptr); + file = VirtFsDir::openRead("tesQ/units.xml"); + REQUIRE(file == nullptr); + file = VirtFsDir::openRead("units.xml"); + REQUIRE(file != nullptr); + VirtFsDir::close(file); +// file = VirtFsDir::openRead("test"); +// REQUIRE(file == nullptr); + file = VirtFsDir::openRead("testQ"); + REQUIRE(file == nullptr); + + VirtFsDir::removeFromSearchPathSilent("data/test"); + VirtFsDir::removeFromSearchPathSilent("../data/test"); + + file = VirtFsDir::openRead("test/units.xml"); + REQUIRE(file != nullptr); + VirtFsDir::close(file); + file = VirtFsDir::openRead("test/units123.xml"); + REQUIRE(file == nullptr); + file = VirtFsDir::openRead("tesQ/units.xml"); + REQUIRE(file == nullptr); + file = VirtFsDir::openRead("units.xml"); + REQUIRE(file == nullptr); +// file = VirtFsDir::openRead("test"); +// REQUIRE(file == nullptr); + file = VirtFsDir::openRead("testQ"); + REQUIRE(file == nullptr); + + VirtFsDir::removeFromSearchPathSilent("data"); + VirtFsDir::removeFromSearchPathSilent("../data"); + VirtFsDir::deinit(); + delete2(logger); +} + + +TEST_CASE("VirtFsDir permitLinks") +{ + logger = new Logger(); + VirtFsDir::addToSearchPathSilent("data", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data", + Append_false, + SkipError_false); + + const int cnt1 = VirtFsDir::exists("test/test2.txt") ? 22 : 21; + const int cnt2 = 22; + + StringVect list; + VirtFsDir::permitLinks(false); + VirtFsDir::getFiles("test", list); + removeTemp(list); + const size_t sz = list.size(); + REQUIRE(sz == cnt1); + + list.clear(); + VirtFsDir::permitLinks(true); + VirtFsDir::getFiles("test", list); + removeTemp(list); + REQUIRE(list.size() == cnt2); + + list.clear(); + VirtFsDir::permitLinks(false); + VirtFsDir::getFiles("test", list); + removeTemp(list); + REQUIRE(list.size() == cnt1); + + VirtFsDir::removeFromSearchPathSilent("data"); + VirtFsDir::removeFromSearchPathSilent("../data"); + delete2(logger); +} + +TEST_CASE("VirtFsDir read") +{ + logger = new Logger(); + VirtFsDir::addToSearchPathSilent("data", + Append_false, + SkipError_false); + VirtFsDir::addToSearchPathSilent("../data", + Append_false, + SkipError_false); + + VirtFile *file = VirtFsDir::openRead("test/test.txt"); + REQUIRE(file != nullptr); + REQUIRE(VirtFsDir::fileLength(file) == 23); + const int fileSize = VirtFsDir::fileLength(file); + + void *restrict buffer = calloc(fileSize + 1, 1); + REQUIRE(VirtFsDir::read(file, buffer, 1, fileSize) == fileSize); + REQUIRE(strcmp(static_cast(buffer), + "test line 1\ntest line 2") == 0); + REQUIRE(VirtFsDir::tell(file) == fileSize); + REQUIRE(VirtFsDir::eof(file) == true); + + free(buffer); + buffer = calloc(fileSize + 1, 1); + REQUIRE(VirtFsDir::seek(file, 12) != 0); + REQUIRE(VirtFsDir::eof(file) == false); + REQUIRE(VirtFsDir::tell(file) == 12); + REQUIRE(VirtFsDir::read(file, buffer, 1, 11) == 11); + REQUIRE(strcmp(static_cast(buffer), + "test line 2") == 0); + REQUIRE(VirtFsDir::eof(file) == true); + + VirtFsDir::close(file); + free(buffer); + + VirtFsDir::removeFromSearchPathSilent("data"); + VirtFsDir::removeFromSearchPathSilent("../data"); + delete2(logger); +} diff --git a/src/fs/virtfstools.cpp b/src/fs/virtfstools.cpp index 889739658..d227f5f07 100644 --- a/src/fs/virtfstools.cpp +++ b/src/fs/virtfstools.cpp @@ -24,6 +24,7 @@ #include "fs/paths.h" #include "fs/virtfs.h" +#include "fs/virtfsdir.h" #include "fs/virtlist.h" #include "utils/stringutils.h" @@ -221,3 +222,19 @@ namespace VirtFs return true; } } // namespace VirtFs + +// +++ temporary add it here +namespace VirtFsDir +{ + void getFiles(const std::string &path, + StringVect &list) + { + VirtList *const fonts = VirtFsDir::enumerateFiles(path); + FOR_EACH (StringVectCIter, i, fonts->names) + { + if (!VirtFsDir::isDirectory(path + dirSeparator + *i)) + list.push_back(*i); + } + VirtFsDir::freeList(fonts); + } +} // namespace VirtFs diff --git a/src/fs/virtfstools.h b/src/fs/virtfstools.h index 4f2a77f4c..e29e3e427 100644 --- a/src/fs/virtfstools.h +++ b/src/fs/virtfstools.h @@ -51,4 +51,11 @@ namespace VirtFs std::string loadTextFileString(const std::string &fileName); } // namespace VirtFs +// +++ temporary add it here +namespace VirtFsDir +{ + void getFiles(const std::string &path, + StringVect &list); +} // namespace VirtFs + #endif // UTILS_VIRTFSTOOLS_H -- cgit v1.2.3-60-g2f50