Write TPR body as opaque XDR data in big-endian format
[gromacs.git] / src / gromacs / utility / directoryenumerator.cpp
blob439473af3ab612d793fa32411844fb575ffcc6f9
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2014,2015,2016,2017,2019, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
35 /*! \internal \file
36 * \brief
37 * Implements gmx::DirectoryEnumerator.
39 * \author Erik Lindahl (original C implementation)
40 * \author Teemu Murtola <teemu.murtola@gmail.com> (C++ wrapper + errno handling)
41 * \ingroup module_utility
43 #include "gmxpre.h"
45 #include "directoryenumerator.h"
47 #include "config.h"
49 #include <cerrno>
50 #include <cstdio>
52 #include <algorithm>
53 #include <string>
54 #include <vector>
56 #if HAVE_DIRENT_H
57 # include <dirent.h>
58 #endif
59 #if GMX_NATIVE_WINDOWS
60 # include <io.h>
61 #endif
63 #include "gromacs/utility/exceptions.h"
64 #include "gromacs/utility/fatalerror.h"
65 #include "gromacs/utility/futil.h"
66 #include "gromacs/utility/gmxassert.h"
67 #include "gromacs/utility/smalloc.h"
68 #include "gromacs/utility/stringutil.h"
70 namespace gmx
73 /********************************************************************
74 * DirectoryEnumerator::Impl
77 // TODO: Consider whether checking the return value of closing would be useful,
78 // and what could we do if it fails?
79 #if GMX_NATIVE_WINDOWS
80 // TODO: Consider if Windows provides more error details through other APIs.
81 class DirectoryEnumerator::Impl
83 public:
84 static Impl* init(const char* dirname, bool bThrow)
86 std::string tmpname(dirname);
87 // Remove possible trailing directory separator.
88 // TODO: Use a method in gmx::Path instead.
89 if (tmpname.back() == '/' || tmpname.back() == '\\')
91 tmpname.pop_back();
94 // Add wildcard.
95 tmpname.append("/*");
97 errno = 0;
98 _finddata_t finddata;
99 intptr_t handle = _findfirst(tmpname.c_str(), &finddata);
100 if (handle < 0L)
102 if (errno != ENOENT && bThrow)
104 const int code = errno;
105 const std::string message =
106 formatString("Failed to list files in directory '%s'", dirname);
107 GMX_THROW_WITH_ERRNO(FileIOError(message), "_findfirst", code);
109 return NULL;
111 return new Impl(handle, finddata);
113 Impl(intptr_t handle, _finddata_t finddata) :
114 windows_handle(handle),
115 finddata(finddata),
116 bFirst_(true)
119 ~Impl() { _findclose(windows_handle); }
121 bool nextFile(std::string* filename)
123 if (bFirst_)
125 *filename = finddata.name;
126 bFirst_ = false;
127 return true;
129 else
131 errno = 0;
132 if (_findnext(windows_handle, &finddata) != 0)
134 if (errno == 0 || errno == ENOENT)
136 filename->clear();
137 return false;
139 else
141 GMX_THROW_WITH_ERRNO(FileIOError("Failed to list files in a directory"),
142 "_findnext", errno);
145 *filename = finddata.name;
146 return true;
150 private:
151 intptr_t windows_handle;
152 _finddata_t finddata;
153 bool bFirst_;
155 #elif HAVE_DIRENT_H
156 class DirectoryEnumerator::Impl
158 public:
159 static Impl* init(const char* dirname, bool bThrow)
161 errno = 0;
162 DIR* handle = opendir(dirname);
163 if (handle == nullptr)
165 if (bThrow)
167 const int code = errno;
168 const std::string message =
169 formatString("Failed to list files in directory '%s'", dirname);
170 GMX_THROW_WITH_ERRNO(FileIOError(message), "opendir", code);
172 return nullptr;
174 return new Impl(handle);
176 explicit Impl(DIR* handle) : dirent_handle(handle) {}
177 ~Impl() { closedir(dirent_handle); }
179 bool nextFile(std::string* filename)
181 errno = 0;
182 dirent* p = readdir(dirent_handle);
183 if (p == nullptr)
185 if (errno == 0)
187 // All the files have been found.
188 filename->clear();
189 return false;
191 else
193 GMX_THROW_WITH_ERRNO(FileIOError("Failed to list files in a directory"), "readdir", errno);
196 *filename = p->d_name;
197 return true;
200 private:
201 DIR* dirent_handle;
203 #else
204 class DirectoryEnumerator::Impl
206 public:
207 static Impl* init(const char* /*dirname*/, bool /*bThrow*/)
209 std::string message(
210 "Source compiled without POSIX dirent or Windows support "
211 "- cannot scan directories. In the very unlikely event "
212 "this is not a compile-time mistake you could consider "
213 "implementing support for your platform in "
214 "directoryenumerator.cpp, but contact the developers "
215 "to make sure it's really necessary!");
216 GMX_THROW(NotImplementedError(message));
219 bool nextFile(std::string* /*filename*/) { return false; }
221 #endif
223 /********************************************************************
224 * DirectoryEnumerator
227 // static
228 std::vector<std::string> DirectoryEnumerator::enumerateFilesWithExtension(const char* dirname,
229 const char* extension,
230 bool bThrow)
232 std::vector<std::string> result;
233 DirectoryEnumerator dir(dirname, bThrow);
234 std::string nextName;
235 while (dir.nextFile(&nextName))
237 if (debug)
239 std::fprintf(debug, "dir '%s' file '%s'\n", dirname, nextName.c_str());
241 // TODO: What about case sensitivity?
242 if (endsWith(nextName, extension))
244 result.push_back(nextName);
248 std::sort(result.begin(), result.end());
249 return result;
253 DirectoryEnumerator::DirectoryEnumerator(const char* dirname, bool bThrow) : impl_(nullptr)
255 GMX_RELEASE_ASSERT(dirname != nullptr && dirname[0] != '\0',
256 "Attempted to open empty/null directory path");
257 impl_.reset(Impl::init(dirname, bThrow));
260 DirectoryEnumerator::DirectoryEnumerator(const std::string& dirname, bool bThrow) : impl_(nullptr)
262 GMX_RELEASE_ASSERT(!dirname.empty(), "Attempted to open empty/null directory path");
263 impl_.reset(Impl::init(dirname.c_str(), bThrow));
266 DirectoryEnumerator::~DirectoryEnumerator() {}
268 bool DirectoryEnumerator::nextFile(std::string* filename)
270 if (impl_ == nullptr)
272 filename->clear();
273 return false;
275 return impl_->nextFile(filename);
278 } // namespace gmx