1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <cmajor/rt/Directory.hpp>
  7 #include <soulng/util/Path.hpp>
  8 #include <memory>
  9 #include <mutex>
 10 #include <unordered_map>
 11 #include <boost/filesystem.hpp>
 12 
 13 namespace cmajor { namespace rt {
 14 
 15 using namespace soulng::util;
 16 
 17 struct Iteration 
 18 {
 19     boost::filesystem::directory_iterator directoryIterator;
 20     std::string directoryName;
 21     std::string path;
 22 };
 23 
 24 class DirectoryIterationTable 
 25 {
 26 public:
 27     static void Init();
 28     static void Done();
 29     static DirectoryIterationTable& Instance() { return *instance; }
 30     int32_t BeginIterate(const char* directoryPath);
 31     const char* IterateFiles(int32_t handle);
 32     const char* IterateDirectories(int32_t handle);
 33     void EndIterate(int32_t handle);
 34 private:
 35     static std::unique_ptr<DirectoryIterationTable> instance;
 36     DirectoryIterationTable();
 37     int32_t nextIterationHandle;
 38     std::unordered_map<int32_tIteration> iterationMap;
 39     std::mutex mtx;
 40 };
 41 
 42 std::unique_ptr<DirectoryIterationTable> DirectoryIterationTable::instance;
 43 
 44 void DirectoryIterationTable::Init()
 45 {
 46     instance.reset(new DirectoryIterationTable());
 47 }
 48 
 49 void DirectoryIterationTable::Done()
 50 {
 51     instance.reset();
 52 }
 53 
 54 DirectoryIterationTable::DirectoryIterationTable() : nextIterationHandle(0)
 55 {
 56 }
 57 
 58 int32_t DirectoryIterationTable::BeginIterate(const char* directoryPath)
 59 {
 60     std::lock_guard<std::mutex> lock(mtx);
 61     int32_t handle = nextIterationHandle++;
 62     Iteration iteration;
 63     iteration.directoryName = GetFullPath(Path::MakeCanonical(directoryPath));
 64     iteration.directoryIterator = boost::filesystem::directory_iterator(iteration.directoryName);
 65     iterationMap[handle] = iteration;
 66     return handle;
 67 }
 68 
 69 void DirectoryIterationTable::EndIterate(int32_t handle)
 70 {
 71     std::lock_guard<std::mutex> lock(mtx);
 72     iterationMap.erase(handle);
 73 }
 74 
 75 const char* DirectoryIterationTable::IterateFiles(int32_t handle)
 76 {
 77     std::lock_guard<std::mutex> lock(mtx);
 78     auto it = iterationMap.find(handle);
 79     if (it != iterationMap.cend())
 80     {
 81         Iteration& iteration = it->second;
 82         while (iteration.directoryIterator != boost::filesystem::directory_iterator() && !boost::filesystem::is_regular_file(*iteration.directoryIterator))
 83         {
 84             ++iteration.directoryIterator;
 85         }
 86         if (iteration.directoryIterator != boost::filesystem::directory_iterator())
 87         {
 88             iteration.path = GetFullPath(Path::Combine(iteration.directoryNameboost::filesystem::path(*iteration.directoryIterator).generic_string()));
 89             ++iteration.directoryIterator;
 90             return iteration.path.c_str();
 91         }
 92         return nullptr;
 93     }
 94     else
 95     {
 96         return nullptr;
 97     }
 98 }
 99 
100 const char* DirectoryIterationTable::IterateDirectories(int32_t handle)
101 {
102     std::lock_guard<std::mutex> lock(mtx);
103     auto it = iterationMap.find(handle);
104     if (it != iterationMap.cend())
105     {
106         Iteration& iteration = it->second;
107         while (iteration.directoryIterator != boost::filesystem::directory_iterator() && 
108             (!boost::filesystem::is_directory(*iteration.directoryIterator) || iteration.directoryIterator->path() == "." || iteration.directoryIterator->path() == ".."))
109         {
110             ++iteration.directoryIterator;
111         }
112         if (iteration.directoryIterator != boost::filesystem::directory_iterator())
113         {
114             iteration.path = GetFullPath(Path::Combine(iteration.directoryNameboost::filesystem::path(*iteration.directoryIterator).generic_string()));
115             ++iteration.directoryIterator;
116             return iteration.path.c_str();
117         }
118         return nullptr;
119     }
120     else
121     {
122         return nullptr;
123     }
124 }
125 
126 void InitDirectory()
127 {
128     DirectoryIterationTable::Init();
129 }
130 
131 void DoneDirectory()
132 {
133     DirectoryIterationTable::Done();
134 }
135 
136 } } // namespace cmajor::rt
137 
138 extern "C" bool RtDirectoryExists(const char* directoryPath)
139 {
140     return boost::filesystem::exists(directoryPath);
141 }
142 
143 extern "C" void RtCreateDirectories(const char* directoryPath)
144 {
145     boost::filesystem::create_directories(directoryPath);
146 }
147 
148 extern "C" int32_t RtBeginIterateDirectory(const char* directoryPath)
149 {
150     return cmajor::rt::DirectoryIterationTable::Instance().BeginIterate(directoryPath);
151 }
152 
153 extern "C" const char* RtGetNextFilePath(int32_t directoryIterationHandle)
154 {
155     return cmajor::rt::DirectoryIterationTable::Instance().IterateFiles(directoryIterationHandle);
156 }
157 
158 extern "C" const char* RtGetNextDirectoryPath(int32_t directoryIterationHandle)
159 {
160     return cmajor::rt::DirectoryIterationTable::Instance().IterateDirectories(directoryIterationHandle);
161 }
162 
163 extern "C" void RtEndIterateDirectory(int32_t directoryIterationHandle)
164 {
165     cmajor::rt::DirectoryIterationTable::Instance().EndIterate(directoryIterationHandle);
166 }