## Filesystem Since C++17 we can efficiently work with the file systems on all OS like Mac, Windows, and various Linux distributions. ```C++ namespace fs = std::filesystem; void printStatsForDirectory(const fs::path& dir_path) { std::cout << "Statistics:\n"; std::cout << ".cpp files: " << std::count_if(fs::recursive_directory_iterator(dir_path), {}, [](const auto& entry) { return entry.is_regular_file() && entry.path().extension() == ".cpp"; }) << '\n'; std::cout << ".md files: " << std::count_if(fs::recursive_directory_iterator(dir_path), {}, [](const auto& entry) { return entry.is_regular_file() && entry.path().extension() == ".md"; }) << '\n'; std::cout << ".h files: " << std::count_if(fs::recursive_directory_iterator(dir_path), {}, [](const auto& entry) { return entry.is_regular_file() && entry.path().extension() == ".h"; }) << '\n'; std::cout << "Total size: " << (std::accumulate(fs::recursive_directory_iterator(dir_path), {}, 0, [](const auto& init, const auto& entry) { return init + (entry.is_regular_file() ? entry.file_size() : 0); }) >> 20) << " MB\n"; } ``` ___ ## Output ```C++ int main() { const auto path{fs::current_path()}; std::cout << "current path: " << path << '\n'; const auto path2 = path.parent_path().parent_path(); std::cout << "path2: " << path2 << '\n'; printStatsForDirectory(path2); } ``` ```bash current path: "C:\\Users\\madamski\\Documents\\altcom_academy\\Nokia2022\\Advanced\\Course_part1\\exercises\\streamer" path2: "C:\\Users\\madamski\\Documents\\altcom_academy\\Nokia2022\\Advanced\\Course_part1" Statistics: .cpp files: 16 .md files: 53 .h files: 47 Total size: 103 MB ``` ___ ## Info about system space ```C++ void printSpaceInfo(auto const& dirs, int width = 10) { std::cout << std::left; for (const auto s : {"Capacity", "Free", "Available", "Dir"}) { std::cout << "| " << std::setw(width) << s << std::string(3, ' '); } std::cout << '\n'; for (auto const& dir : dirs) { const auto info = fs::space(dir); std::cout << "| " << std::setw(width) << (info.capacity >> 30) << " GB" << "| " << std::setw(width) << (info.free >> 30) << " GB" << "| " << std::setw(width) << (info.available >> 30) << " GB" << "| " << dir << '\n'; } } int main() { const auto dirs = {"C:\\", "D:\\", "E:\\"}; printSpaceInfo(dirs); /* | Capacity | Free | Available | Dir | 237 GB| 15 GB| 15 GB| C:\ | 383 GB| 179 GB| 179 GB| D:\ | 463 GB| 220 GB| 220 GB| E:\ */ } ``` ___ ## Permissions ```C++ void printPerms(fs::perms p) { std::cout << ((p & fs::perms::owner_read) != fs::perms::none ? "r" : "-") << ((p & fs::perms::owner_write) != fs::perms::none ? "w" : "-") << ((p & fs::perms::owner_exec) != fs::perms::none ? "x" : "-") << ((p & fs::perms::group_read) != fs::perms::none ? "r" : "-") << ((p & fs::perms::group_write) != fs::perms::none ? "w" : "-") << ((p & fs::perms::group_exec) != fs::perms::none ? "x" : "-") << ((p & fs::perms::others_read) != fs::perms::none ? "r" : "-") << ((p & fs::perms::others_write) != fs::perms::none ? "w" : "-") << ((p & fs::perms::others_exec) != fs::perms::none ? "x" : "-") << '\n'; } int main() { for (const auto& entry : fs::directory_iterator(fs::current_path())) { if (fs::is_regular_file(entry.path())) { std::cout << "File: " << std::left << std::setw(20) << entry.path().filename() << " | Permissions: "; printPerms(fs::status(entry.path()).permissions()); } } /* File: "CMakeLists.txt" | Permissions: rw-rw-rw- File: "example.exe" | Permissions: rwxrwxrwx File: "README.md" | Permissions: rw-rw-rw- File: "streamer.cpp" | Permissions: rw-rw-rw- */ } ``` ___ ## Exercise Open project `files`. Write a program which modify files under `project` directory: - Change permision for all .exe files to `rwxrw-rw-`. - Change permision for all .cpp and .hpp files to `rw-r--r--`. You should get output like this: ```Bash File: "fileA.cpp" | Permissions: rw------- File: "fileA.hpp" | Permissions: rw------- File: "fileB.cpp" | Permissions: rw------- File: "fileB.hpp" | Permissions: rw------- File: "fileC.cpp" | Permissions: rw------- File: "fileC.hpp" | Permissions: rw------- File: "project.exe" | Permissions: rw------- File: "fileA.cpp" | Permissions: rw-r--r-- File: "fileA.hpp" | Permissions: rw-r--r-- File: "fileB.cpp" | Permissions: rw-r--r-- File: "fileB.hpp" | Permissions: rw-r--r-- File: "fileC.cpp" | Permissions: rw-r--r-- File: "fileC.hpp" | Permissions: rw-r--r-- File: "project.exe" | Permissions: rwxrw-rw- ``` ___ ## Exercise 2 Open project `files`. Modify `FileB` by append any word. Now write a program which: - return last modify files - return a vector with 3 files that were longest unmodified. Expected result (path may by different): ```Bash Last modified file is: "/home/runner/SilentSiennaModularity/files/project/fileB.cpp" File write time is Sat May 14 17:56:18 2022 files that were longest unmodified: File: "/home/runner/SilentSiennaModularity/files/project/fileA.cpp" | last modify time: Sat May 14 16:43:33 2022 File: "/home/runner/SilentSiennaModularity/files/project/fileA.hpp" | last modify time: Sat May 14 16:43:33 2022 File: "/home/runner/SilentSiennaModularity/files/project/fileB.hpp" | last modify time: Sat May 14 16:43:33 2022 ``` ___ ## Problems and fixes - Remember that recursive_directory_iterator and directory_iterator are InputIterator. If you move iterator to the next position you invalidate all reference to previous object. If you need to use algorithm which demand ForwardIterator you can copy all paths to separate container - If you want to keep sorted files inside set or map you should check whether their modification times are the same. If true you need to compare their names or files with the same modification time will be lost. - Setup premissions for files works only on Linux/ MacOS system. This is caused by different type of File system. Windows file system mainly NTFS do not support priviliges like in other systems eg. EXT4. There are still properites but handled in different way: - Full Control: Grants complete access, including the ability to see, read, write, execute and delete files or folders, as well as change permission settings for all subdirectories. - Modify: The user can see, read, execute, write and delete files. Also allows for the deletion of the folder itself. - Read & Execute: Can view folder contents and run programs or scripts. - List folder contents: Allows the user to see files and directories contained within a folder, an important setting for navigating to deeper levels in the folder structure. - Read: Can see folder contents and also view the files and folders in question. - Write: Users can add new files and folders and write to existing files. - Special permissions: Additional permissions available through the Advanced Security Settings in the Windows file system. Includes options such as Read Attributes, Create Files or Traverse Folder. ___ ## Temporary files for testing Sometimes during a test we need to read input data from file. usually we need to create this file and attach to test directory. But since C++17 we can easily use temporary directory to create all files. let's test this class: (The main resposibility is to queued all files and stream them) ```C++ class Streamer { public: Streamer() = default; virtual ~Streamer() = default; Streamer(const Streamer&) = default; Streamer(Streamer&&) = default; Streamer& operator=(const Streamer&) = default; Streamer& operator=(Streamer&&) = default; virtual void stream() = 0; virtual void stop() = 0; }; ``` ___
```C++ class MpgegStreamer : public Streamer { public: MpgegStreamer(const fs::path& path) : path_(path) { for (int i = 0; i < 4; ++i) { workers_.emplace_back(&MpgegStreamer::work, this, i); } } ~MpgegStreamer() override { for (auto& th : workers_) { th.join(); } } void stream() override { for (const auto& entry : fs::directory_iterator(path_)) { std::lock_guard lg(m_); streamQueue_.push(entry.path()); } } virtual void stop() { finishAction_ = true; } ```
```C++ private: void work(int id) { while (!finishAction_) { fs::path path; { std::lock_guard lg(m_); // Ofc this will be better with condition variable if (streamQueue_.empty()) { continue; } path = std::move(streamQueue_.front()); streamQueue_.pop(); } stream(path, id); } } void stream(const fs::path path, int id) const { std::fstream fileToStream(path); std::transform( std::istream_iterator(fileToStream), {}, std::ostream_iterator(std::cout), [id](const std::string& str) { std::stringstream ss; ss << "Streamer: " << id << " | data: " << str << '\n'; return ss.str(); }); } mutable std::mutex m_; std::vector workers_; std::queue streamQueue_; fs::path path_; std::atomic finishAction_{false}; }; ```
___ We can create temporary direcotories (yes we can create them recursive!) and cleanup at the end. We don't need to be afraid of removing sth neccessary because we will work only with tmoprary directories. ```C++ int main() { const auto tmp_path = fs::temp_directory_path(); const auto mpegDir = tmp_path.string() + "Test/MpgegFiles"; const auto mp4Dir = tmp_path.string() + "Test/MP4Files"; // Create test directory fs::create_directories(mpegDir); fs::create_directory(mp4Dir); // Add files for (int i = 0; i < 10; ++i) { std::ofstream file(mpegDir + "/File" + std::to_string(i)); file << "this is some text in the new file\n" << i; } for (int i = 0; i < 10; ++i) { std::ofstream file(mp4Dir + "/File" + std::to_string(i)); file << "this is some text in the new file\n" << i; } std::cout << "Start streaming" << std::endl; std::unique_ptr streamer = std::make_unique(mpegDir); streamer->stream(); std::this_thread::sleep_for(std::chrono::seconds(1)); streamer->stop(); fs::remove_all(tmp_path.string() + "Test"); } ```