From d1b635ebf238fac5911fde8982d067ee4ffffe3a Mon Sep 17 00:00:00 2001
From: Andrei Karas <akaras@inbox.ru>
Date: Mon, 24 Apr 2017 17:04:47 +0300
Subject: Improve VirtFs::getDirs.

---
 src/fs/virtfs/virtfs.cpp          | 22 +++++++++
 src/fs/virtfs/virtfs.h            |  4 +-
 src/fs/virtfs/virtfs1_unittest.cc | 77 +++++++++++++++++++++++++++++++-
 src/fs/virtfs/virtfsdir.cpp       | 49 +++++++++++++++++++++
 src/fs/virtfs/virtfsdir.h         |  3 ++
 src/fs/virtfs/virtfsfuncs.h       |  3 ++
 src/fs/virtfs/virtfstools.cpp     | 11 -----
 src/fs/virtfs/virtfstools.h       |  2 -
 src/fs/virtfs/virtfszip.cpp       | 93 +++++++++++++++++++++++++++++++++++++++
 src/fs/virtfs/virtfszip.h         |  3 ++
 10 files changed, 252 insertions(+), 15 deletions(-)

(limited to 'src')

diff --git a/src/fs/virtfs/virtfs.cpp b/src/fs/virtfs/virtfs.cpp
index e61069301..b4807afe7 100644
--- a/src/fs/virtfs/virtfs.cpp
+++ b/src/fs/virtfs/virtfs.cpp
@@ -183,6 +183,28 @@ namespace VirtFs
         }
     }
 
+    void getDirs(std::string dirName,
+                 StringVect &list)
+    {
+        prepareFsPath(dirName);
+        if (checkPath(dirName) == false)
+        {
+            reportAlways("VirtFs::enumerateFiles invalid path: %s",
+                dirName.c_str());
+            return;
+        }
+
+        std::string rootDir = dirName;
+        if (findLast(rootDir, std::string(dirSeparator)) == false)
+            rootDir += dirSeparator;
+
+        FOR_EACH (std::vector<VirtFsEntry*>::const_iterator, it, mEntries)
+        {
+            VirtFsEntry *const entry = *it;
+            entry->funcs->getDirs(entry, rootDir, list);
+        }
+    }
+
     bool isDirectory(std::string name)
     {
         prepareFsPath(name);
diff --git a/src/fs/virtfs/virtfs.h b/src/fs/virtfs/virtfs.h
index 7f9600b88..5046f4675 100644
--- a/src/fs/virtfs/virtfs.h
+++ b/src/fs/virtfs/virtfs.h
@@ -94,8 +94,10 @@ namespace VirtFs
 #endif  // UNITTESTS
     const char *loadFile(std::string filename,
                          int &restrict fileSize);
-    void getFiles(std::string path,
+    void getFiles(std::string dirName,
                   StringVect &list);
+    void getDirs(std::string dirName,
+                 StringVect &list);
 }  // namespace VirtFs
 
 extern const char *dirSeparator;
diff --git a/src/fs/virtfs/virtfs1_unittest.cc b/src/fs/virtfs/virtfs1_unittest.cc
index a267f2b5a..23255e0c5 100644
--- a/src/fs/virtfs/virtfs1_unittest.cc
+++ b/src/fs/virtfs/virtfs1_unittest.cc
@@ -1935,13 +1935,88 @@ TEST_CASE("VirtFs1 getFiles zip")
 
     list.clear();
     VirtFs::getFiles("dir2", list);
+    REQUIRE(list.size() == 4);
     REQUIRE(inList(list, "hide.png"));
     REQUIRE(inList(list, "paths.xml"));
     REQUIRE(inList(list, "test.txt"));
     REQUIRE(inList(list, "units.xml"));
-    REQUIRE(list.size() == 4);
 
     VirtFs::unmountZip(prefix + "data/test/test2.zip");
     VirtFs::deinit();
     delete2(logger);
 }
+
+TEST_CASE("VirtFs1 getDirs1")
+{
+    VirtFs::init(".");
+    logger = new Logger();
+    std::string name("data/test/test.zip");
+    std::string prefix;
+    if (Files::existsLocal(name) == false)
+        prefix = "../" + prefix;
+
+    VirtFs::mountZip(prefix + "data/test/test2.zip",
+        Append_false);
+
+    StringVect list;
+    VirtFs::getDirs("dir", list);
+    REQUIRE(list.size() == 2);
+    REQUIRE(inList(list, "1"));
+    REQUIRE(inList(list, "gpl"));
+    list.clear();
+
+    VirtFs::getDirs("dir2", list);
+    REQUIRE(list.size() == 0);
+
+    VirtFs::unmountZip(prefix + "data/test/test2.zip");
+    VirtFs::deinit();
+    delete2(logger);
+}
+
+TEST_CASE("VirtFs1 getDirs2")
+{
+    VirtFs::init(".");
+    logger = new Logger();
+    std::string name("data/test/test.zip");
+    std::string prefix;
+    if (Files::existsLocal(name) == false)
+        prefix = "../" + prefix;
+    StringVect list;
+
+    SECTION("dir1")
+    {
+        VirtFs::mountDir(prefix + "data/test",
+            Append_false);
+
+        VirtFs::getDirs("/", list);
+//        REQUIRE(list.size() == 2);
+        REQUIRE(inList(list, "dir1"));
+        REQUIRE(inList(list, "dir2"));
+        list.clear();
+
+        VirtFs::getDirs("dir1", list);
+        REQUIRE(list.size() == 0);
+
+        VirtFs::unmountDir(prefix + "data/test");
+    }
+
+    SECTION("dir2")
+    {
+        VirtFs::mountDir(prefix + "data",
+            Append_false);
+
+        VirtFs::getDirs("sfx", list);
+        REQUIRE(inList(list, "system"));
+        list.clear();
+
+        VirtFs::getDirs("evol", list);
+        REQUIRE(list.size() == 2);
+        REQUIRE(inList(list, "icons"));
+        REQUIRE(inList(list, "images"));
+
+        VirtFs::unmountDir(prefix + "data");
+    }
+
+    VirtFs::deinit();
+    delete2(logger);
+}
diff --git a/src/fs/virtfs/virtfsdir.cpp b/src/fs/virtfs/virtfsdir.cpp
index 2d165195e..967c80610 100644
--- a/src/fs/virtfs/virtfsdir.cpp
+++ b/src/fs/virtfs/virtfsdir.cpp
@@ -138,6 +138,7 @@ namespace VirtFsDir
         ptr->openAppend = &VirtFsDir::openAppend;
         ptr->loadFile = &VirtFsDir::loadFile;
         ptr->getFiles = &VirtFsDir::getFiles;
+        ptr->getDirs = &VirtFsDir::getDirs;
         ptr->rwops_seek = &VirtFsDir::rwops_seek;
         ptr->rwops_read = &VirtFsDir::rwops_read;
         ptr->rwops_write = &VirtFsDir::rwops_write;
@@ -562,4 +563,52 @@ namespace VirtFsDir
         }
     }
 
+    void getDirs(VirtFsEntry *restrict const entry,
+                 const std::string &dirName,
+                 StringVect &names)
+    {
+        const std::string path = entry->root + dirName;
+        const struct dirent *next_file = nullptr;
+        DIR *const dir = opendir(path.c_str());
+        if (dir)
+        {
+            while ((next_file = readdir(dir)))
+            {
+                struct stat statbuf;
+                const std::string file = next_file->d_name;
+                if (file == "." || file == "..")
+                    continue;
+#ifndef WIN32
+                if (mPermitLinks == false)
+                {
+                    if (lstat(path.c_str(), &statbuf) == 0 &&
+                        S_ISLNK(statbuf.st_mode) != 0)
+                    {
+                        continue;
+                    }
+                }
+#endif  // WIN32
+
+                const std::string filePath = pathJoin(path, file);
+                if (stat(filePath.c_str(), &statbuf) == 0)
+                {
+                    if (S_ISDIR(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);
+        }
+    }
 }  // namespace VirtFs
diff --git a/src/fs/virtfs/virtfsdir.h b/src/fs/virtfs/virtfsdir.h
index 2a7829bb1..74bf569bf 100644
--- a/src/fs/virtfs/virtfsdir.h
+++ b/src/fs/virtfs/virtfsdir.h
@@ -58,6 +58,9 @@ namespace VirtFsDir
     void getFiles(VirtFsEntry *restrict const entry,
                   const std::string &dirName,
                   StringVect &names);
+    void getDirs(VirtFsEntry *restrict const entry,
+                 const std::string &dirName,
+                 StringVect &names);
     bool isDirectory(VirtFsEntry *restrict const entry,
                      const std::string &dirName,
                      bool &isDirFlag);
diff --git a/src/fs/virtfs/virtfsfuncs.h b/src/fs/virtfs/virtfsfuncs.h
index 47d765c5c..f8c69e125 100644
--- a/src/fs/virtfs/virtfsfuncs.h
+++ b/src/fs/virtfs/virtfsfuncs.h
@@ -88,6 +88,9 @@ struct VirtFsFuncs final
     void (*getFiles) (VirtFsEntry *restrict const entry,
                       const std::string &dirName,
                       StringVect &names);
+    void (*getDirs) (VirtFsEntry *restrict const entry,
+                     const std::string &dirName,
+                     StringVect &names);
     bool (*isDirectory) (VirtFsEntry *restrict const entry,
                          const std::string &dirName,
                          bool &isDirFlag);
diff --git a/src/fs/virtfs/virtfstools.cpp b/src/fs/virtfs/virtfstools.cpp
index 12b261320..be634b89c 100644
--- a/src/fs/virtfs/virtfstools.cpp
+++ b/src/fs/virtfs/virtfstools.cpp
@@ -104,17 +104,6 @@ namespace VirtFs
         std::sort(list.begin(), list.end());
     }
 
-    void getDirs(const std::string &path, StringVect &list)
-    {
-        VirtList *const fonts = VirtFs::enumerateFiles(path);
-        FOR_EACH (StringVectCIter, i, fonts->names)
-        {
-            if (VirtFs::isDirectory(pathJoin(path, *i)))
-                list.push_back(*i);
-        }
-        VirtFs::freeList(fonts);
-    }
-
     std::string getPath(const std::string &file)
     {
         // get the real path to the file
diff --git a/src/fs/virtfs/virtfstools.h b/src/fs/virtfs/virtfstools.h
index fe9eb7ecf..8385920cc 100644
--- a/src/fs/virtfs/virtfstools.h
+++ b/src/fs/virtfs/virtfstools.h
@@ -39,8 +39,6 @@ namespace VirtFs
                        const std::string &ext);
     void getFilesWithDir(const std::string &restrict path,
                          StringVect &restrict list);
-    void getDirs(const std::string &path,
-                 StringVect &list);
     std::string getPath(const std::string &file);
     bool loadTextFile(const std::string &fileName,
                       StringVect &lines);
diff --git a/src/fs/virtfs/virtfszip.cpp b/src/fs/virtfs/virtfszip.cpp
index ae31f2606..bf7e8fba2 100644
--- a/src/fs/virtfs/virtfszip.cpp
+++ b/src/fs/virtfs/virtfszip.cpp
@@ -75,6 +75,7 @@ namespace VirtFsZip
         ptr->openAppend = &VirtFsZip::openAppend;
         ptr->loadFile = &VirtFsZip::loadFile;
         ptr->getFiles = &VirtFsZip::getFiles;
+        ptr->getDirs = &VirtFsZip::getDirs;
         ptr->rwops_seek = &VirtFsZip::rwops_seek;
         ptr->rwops_read = &VirtFsZip::rwops_read;
         ptr->rwops_write = &VirtFsZip::rwops_write;
@@ -287,6 +288,98 @@ namespace VirtFsZip
         }
     }
 
+    void getDirs(VirtFsEntry *restrict const entry,
+                 const std::string &dirName,
+                 StringVect &names)
+    {
+        VirtZipEntry *const zipEntry = static_cast<VirtZipEntry*>(entry);
+        if (dirName == dirSeparator)
+        {
+            FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+                      it2,
+                      zipEntry->mHeaders)
+            {
+                ZipLocalHeader *const header = *it2;
+                std::string fileName = header->fileName;
+                // skip subdirs from enumeration
+                const size_t idx = fileName.find(dirSeparator);
+                if (idx != std::string::npos)
+                    fileName.erase(idx);
+                bool found(false);
+                FOR_EACH (StringVectCIter, itn, names)
+                {
+                    if (*itn == fileName)
+                    {
+                        found = true;
+                        break;
+                    }
+                }
+                if (found == false)
+                {
+                    std::string dirName2 = pathJoin(dirName, fileName);
+                    if (findLast(dirName2, std::string(dirSeparator)) == false)
+                        dirName2 += dirSeparator;
+                    FOR_EACH (std::vector<std::string>::const_iterator,
+                              it,
+                              zipEntry->mDirs)
+                    {
+                        if (*it == dirName2)
+                        {
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (found == true)
+                        names.push_back(fileName);
+                }
+            }
+        }
+        else
+        {
+            FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+                      it2,
+                      zipEntry->mHeaders)
+            {
+                ZipLocalHeader *const header = *it2;
+                std::string fileName = header->fileName;
+                if (findCutFirst(fileName, dirName) == true)
+                {
+                    // skip subdirs from enumeration
+                    const size_t idx = fileName.find(dirSeparator);
+                    if (idx != std::string::npos)
+                        fileName.erase(idx);
+                    bool found(false);
+                    FOR_EACH (StringVectCIter, itn, names)
+                    {
+                        if (*itn == fileName)
+                        {
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (found == false)
+                    {
+                        std::string dirName2 = pathJoin(dirName, fileName);
+                        if (findLast(dirName2, std::string(dirSeparator)) == false)
+                            dirName2 += dirSeparator;
+                        FOR_EACH (std::vector<std::string>::const_iterator,
+                                  it,
+                                  zipEntry->mDirs)
+                        {
+                            if (*it == dirName2)
+                            {
+                                found = true;
+                                break;
+                            }
+                        }
+                        if (found == true)
+                            names.push_back(fileName);
+                    }
+                }
+            }
+        }
+    }
+
     bool isDirectory(VirtFsEntry *restrict const entry,
                      const std::string &dirName,
                      bool &isDirFlag)
diff --git a/src/fs/virtfs/virtfszip.h b/src/fs/virtfs/virtfszip.h
index 8efa82a50..3ae74cc95 100644
--- a/src/fs/virtfs/virtfszip.h
+++ b/src/fs/virtfs/virtfszip.h
@@ -45,6 +45,9 @@ namespace VirtFsZip
     void getFiles(VirtFsEntry *restrict const entry,
                   const std::string &dirName,
                   StringVect &names);
+    void getDirs(VirtFsEntry *restrict const entry,
+                 const std::string &dirName,
+                 StringVect &names);
     bool isDirectory(VirtFsEntry *restrict const entry,
                      const std::string &dirName,
                      bool &isDirFlag);
-- 
cgit v1.2.3-70-g09d2