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