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 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 [=]<typename... Params>(Params&&... params) {
90 action(std::forward<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 if (type == DT_REG) {
120 Stat dummy;
121 if (!invokeFileAction(path, file, dummy)) {
122 return false; // aborted
123 }
124 } else if (type == DT_DIR) {
125 Stat dummy;
126 if (!invokeDirAction(path, file, dummy)) {
127 return false;
128 }
129 } else {
130 if (auto st = getStat(path)) {
131 if (isRegularFile(*st)) {
132 if (!invokeFileAction(path, file, *st)) {
133 return false;
134 }
135 } else if (isDirectory(*st)) {
136 if (!invokeDirAction(path, file, *st)) {
137 return false;
138 }
139 }
140 }
141 }
142
143 path.resize(origLen);
144 }
145 if (addSlash) path.pop_back();
146
147 return true; // finished normally
148 }
149 }
150
151 template<typename FileAction>
152 bool foreach_file(std::string path, FileAction fileAction)
153 {
154 auto dirAction = [&](const std::string& /*dirPath*/) { /*nothing*/ };
155 return detail::foreach_dirent(path, fileAction, dirAction);
156 }
157
158 template<typename FileAction, typename DirAction>
159 bool foreach_file_and_directory(std::string path, FileAction fileAction, DirAction dirAction)
160 {
161 return detail::foreach_dirent(path, fileAction, dirAction);
162 }
163
164 template<typename FileAction>
165 bool foreach_file_recursive(std::string path, FileAction fileAction)
166 {
167 std::function<bool(std::string&)> dirAction;
168 dirAction = [&](std::string& dirPath) {
169 return detail::foreach_dirent(dirPath, fileAction, dirAction);
170 };
171 return dirAction(path);
172 }
173
174} // namespace openmsx
175
176#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)