2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2014,2015, 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.
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
45 #include "directoryenumerator.h"
59 #ifdef GMX_NATIVE_WINDOWS
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"
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 defined GMX_NATIVE_WINDOWS
80 // TODO: Consider if Windows provides more error details through other APIs.
81 class DirectoryEnumerator::Impl
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() == '\\')
99 intptr_t handle = _findfirst(tmpname.c_str(), &finddata);
102 if (errno != ENOENT && bThrow)
104 const int code = errno;
105 const std::string message =
106 formatString("Failed to list files in directory '%s'",
108 GMX_THROW_WITH_ERRNO(FileIOError(message), "_findfirst", code);
112 return new Impl(handle, finddata);
114 Impl(intptr_t handle, _finddata_t finddata)
115 : windows_handle(handle), finddata(finddata), bFirst_(true)
120 _findclose(windows_handle);
123 bool nextFile(std::string *filename)
127 *filename = finddata.name;
134 if (_findnext(windows_handle, &finddata) != 0)
136 if (errno == 0 || errno == ENOENT)
143 GMX_THROW_WITH_ERRNO(
144 FileIOError("Failed to list files in a directory"),
148 *filename = finddata.name;
154 intptr_t windows_handle;
155 _finddata_t finddata;
158 #elif defined HAVE_DIRENT_H
159 class DirectoryEnumerator::Impl
162 static Impl *init(const char *dirname, bool bThrow)
165 DIR *handle = opendir(dirname);
170 const int code = errno;
171 const std::string message =
172 formatString("Failed to list files in directory '%s'",
174 GMX_THROW_WITH_ERRNO(FileIOError(message), "opendir", code);
178 return new Impl(handle);
180 explicit Impl(DIR *handle) : dirent_handle(handle)
182 // TODO: Use memory allocation that throws, and handle
183 // exception safety (close handle) in such a case.
184 /* On some platforms no space is present for d_name in dirent.
185 * Since d_name is guaranteed to be the last entry, allocating
186 * extra space for dirent will allow more size for d_name.
187 * GMX_MAX_PATH should always be >= the max possible d_name.
189 smalloc(direntp_large, sizeof(*direntp_large) + GMX_PATH_MAX);
193 sfree(direntp_large);
194 closedir(dirent_handle);
197 bool nextFile(std::string *filename)
201 int rc = readdir_r(dirent_handle, direntp_large, &p);
202 if (p == NULL && rc == 0)
209 GMX_THROW_WITH_ERRNO(
210 FileIOError("Failed to list files in a directory"),
213 *filename = direntp_large->d_name;
219 dirent *direntp_large;
222 class DirectoryEnumerator::Impl
225 static Impl *init(const char * /*dirname*/, bool /*bThrow*/)
228 "Source compiled without POSIX dirent or Windows support "
229 "- cannot scan directories. In the very unlikely event "
230 "this is not a compile-time mistake you could consider "
231 "implementing support for your platform in "
232 "directoryenumerator.cpp, but contact the developers "
233 "to make sure it's really necessary!");
234 GMX_THROW(NotImplementedError(message));
237 bool nextFile(std::string * /*filename*/)
244 /********************************************************************
245 * DirectoryEnumerator
249 std::vector<std::string>
250 DirectoryEnumerator::enumerateFilesWithExtension(
251 const char *dirname, const char *extension, bool bThrow)
253 std::vector<std::string> result;
254 DirectoryEnumerator dir(dirname, bThrow);
255 std::string nextName;
256 while (dir.nextFile(&nextName))
260 std::fprintf(debug, "dir '%s' file '%s'\n",
261 dirname, nextName.c_str());
263 // TODO: What about case sensitivity?
264 if (endsWith(nextName, extension))
266 result.push_back(nextName);
270 std::sort(result.begin(), result.end());
275 DirectoryEnumerator::DirectoryEnumerator(const char *dirname, bool bThrow)
278 GMX_RELEASE_ASSERT(dirname != NULL && dirname[0] != '\0',
279 "Attempted to open empty/null directory path");
280 impl_.reset(Impl::init(dirname, bThrow));
283 DirectoryEnumerator::DirectoryEnumerator(const std::string &dirname, bool bThrow)
286 GMX_RELEASE_ASSERT(!dirname.empty(),
287 "Attempted to open empty/null directory path");
288 impl_.reset(Impl::init(dirname.c_str(), bThrow));
291 DirectoryEnumerator::~DirectoryEnumerator()
295 bool DirectoryEnumerator::nextFile(std::string *filename)
302 return impl_->nextFile(filename);