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