openMSX
foreach_file.hh
Go to the documentation of this file.
1#ifndef FOREACH_FILE_HH
2#define FOREACH_FILE_HH
3
4#include "FileOperations.hh"
5#include "ReadDir.hh"
6#include "StringOp.hh"
7#include "one_of.hh"
8#include <functional>
9#include <string_view>
10#include <tuple>
11#include <type_traits>
12
50namespace openmsx {
51 namespace detail {
52 // Take an action that accepts the following parameters:
53 // - a 'std::string&' or 'const std::string&' parameter
54 // - optionally a std::string_view parameter
55 // - optionally a 'const FileOperations::Stat&' parameter
56 // And returns a wrapper that makes the 2nd and 3rd parameter
57 // non-optional, but (possibly) ignores those parameters when
58 // delegating to the wrapped action.
59 template<typename Action>
60 [[nodiscard]] auto adaptParams(Action action) {
61 if constexpr (std::is_invocable_v<Action, std::string&, std::string_view, const FileOperations::Stat&>) {
62 return std::tuple(action, true);
63 } else if constexpr (std::is_invocable_v<Action, std::string&, std::string_view>) {
64 return std::tuple([action](std::string& p, std::string_view f, const FileOperations::Stat& /*st*/) {
65 return action(p, f);
66 }, false);
67 } else if constexpr (std::is_invocable_v<Action, std::string&, const FileOperations::Stat&>) {
68 return std::tuple([action](std::string& p, std::string_view /*f*/, const FileOperations::Stat& st) {
69 return action(p, st);
70 }, true);
71 } else if constexpr (std::is_invocable_v<Action, std::string&>) {
72 return std::tuple([action](std::string& p, std::string_view /*f*/, const FileOperations::Stat& /*st*/) {
73 return action(p);
74 }, false);
75 } else {
76 static_assert((Action{}, false), "Wrong signature for action");
77 }
78 }
79
80 // Take an action and
81 // - return it unchanged if it already returned a non-void result
82 // - otherwise return a wrapper that invokes the given action and then returns 'true'.
83 template<typename Action>
84 [[nodiscard]] auto adaptReturn(Action action) {
85 using ResultType = std::invoke_result_t<Action, std::string&, std::string_view, const FileOperations::Stat&>;
86 if constexpr (std::is_same_v<ResultType, void>) {
87 return [=]<typename... Params>(Params&&... params) {
88 action(std::forward<Params>(params)...);
89 return true; // continue directory-traversal
90 };
91 } else {
92 return action;
93 }
94 }
95
96 template<typename FileAction, typename DirAction>
97 bool foreach_dirent(std::string& path, FileAction fileAction, DirAction dirAction) {
98 auto [invokeFile, statFile] = adaptParams(fileAction);
99 auto [invokeDir, statDir ] = adaptParams(dirAction);
100 auto invokeFileAction = adaptReturn(invokeFile);
101 auto invokeDirAction = adaptReturn(invokeDir);
102 bool needStat = statFile || statDir;
103
104 ReadDir dir(path);
105 bool addSlash = !path.empty() && (path.back() != '/');
106 if (addSlash) path += '/';
107 auto origLen = path.size();
108 while (dirent* d = dir.getEntry()) {
109 std::string_view f(d->d_name);
110 if (f == one_of(".", "..")) continue;
111 path += f;
112 auto file = std::string_view(path).substr(origLen);
113
114 // When the action is not interested in 'Stat' and on operation systems
115 // that support it: avoid doing an extra 'stat()' system call.
116 auto type = needStat ? static_cast<unsigned char>(DT_UNKNOWN) : d->d_type;
117 if (type == DT_REG) {
119 if (!invokeFileAction(path, file, dummy)) {
120 return false; // aborted
121 }
122 } else if (type == DT_DIR) {
124 if (!invokeDirAction(path, file, dummy)) {
125 return false;
126 }
127 } else {
128 if (auto st = FileOperations::getStat(path)) {
130 if (!invokeFileAction(path, file, *st)) {
131 return false;
132 }
133 } else if (FileOperations::isDirectory(*st)) {
134 if (!invokeDirAction(path, file, *st)) {
135 return false;
136 }
137 }
138 }
139 }
140
141 path.resize(origLen);
142 }
143 if (addSlash) path.pop_back();
144
145 return true; // finished normally
146 }
147 }
148
149 template<typename FileAction>
150 bool foreach_file(std::string path, FileAction fileAction)
151 {
152 auto dirAction = [&](const std::string& /*dirPath*/) { /*nothing*/ };
153 return detail::foreach_dirent(path, fileAction, dirAction);
154 }
155
156 template<typename FileAction, typename DirAction>
157 bool foreach_file_and_directory(std::string path, FileAction fileAction, DirAction dirAction)
158 {
159 return detail::foreach_dirent(path, fileAction, dirAction);
160 }
161
162 template<typename FileAction>
163 bool foreach_file_recursive(std::string path, FileAction fileAction)
164 {
165 std::function<bool(std::string&)> dirAction;
166 dirAction = [&](std::string& dirPath) {
167 return detail::foreach_dirent(dirPath, fileAction, dirAction);
168 };
169 return dirAction(path);
170 }
171
172} // namespace openmsx
173
174#endif
Simple wrapper around opendir() / readdir() / closedir() functions.
Definition ReadDir.hh:16
struct dirent * getEntry()
Get directory entry for next file.
Definition ReadDir.cc:17
Definition join.hh:10
bool isRegularFile(const Stat &st)
bool isDirectory(const Stat &st)
std::optional< Stat > getStat(zstring_view filename)
Call stat() and return the stat structure.
bool foreach_dirent(std::string &path, FileAction fileAction, DirAction dirAction)
auto adaptReturn(Action action)
auto adaptParams(Action action)
This file implemented 3 utility functions:
Definition Autofire.cc:11
bool foreach_file_recursive(std::string path, FileAction fileAction)
bool foreach_file(std::string path, FileAction fileAction)
bool foreach_file_and_directory(std::string path, FileAction fileAction, DirAction dirAction)