More fixes from clang-tidy-7
[gromacs.git] / src / gromacs / utility / datafilefinder.cpp
blob9ec5431537695c077ba9cfee837a99eeb6caa50f
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2014,2015,2016,2017,2018, 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::DataFileFinder.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_utility
42 #include "gmxpre.h"
44 #include "datafilefinder.h"
46 #include <cstdlib>
48 #include <set>
49 #include <string>
50 #include <vector>
52 #include "buildinfo.h"
53 #include "gromacs/utility/directoryenumerator.h"
54 #include "gromacs/utility/exceptions.h"
55 #include "gromacs/utility/filestream.h"
56 #include "gromacs/utility/path.h"
57 #include "gromacs/utility/programcontext.h"
58 #include "gromacs/utility/stringutil.h"
60 namespace gmx
63 /********************************************************************
64 * DataFileFinder::Impl
67 class DataFileFinder::Impl
69 public:
70 static std::string getDefaultPath();
72 Impl() : envName_(nullptr), bEnvIsSet_(false) {}
74 const char *envName_;
75 bool bEnvIsSet_;
76 std::vector<std::string> searchPath_;
79 std::string DataFileFinder::Impl::getDefaultPath()
81 const InstallationPrefixInfo installPrefix
82 = getProgramContext().installationPrefix();
83 if (!isNullOrEmpty(installPrefix.path))
85 const char *const dataPath
86 = installPrefix.bSourceLayout ? "share" : GMX_INSTALL_GMXDATADIR;
87 return Path::join(installPrefix.path, dataPath, "top");
89 return std::string();
92 /********************************************************************
93 * DataFileFinder
96 DataFileFinder::DataFileFinder()
97 : impl_(nullptr)
101 DataFileFinder::~DataFileFinder()
105 void DataFileFinder::setSearchPathFromEnv(const char *envVarName)
107 if (!impl_)
109 impl_.reset(new Impl());
111 impl_->envName_ = envVarName;
112 const char *const lib = getenv(envVarName);
113 if (!isNullOrEmpty(lib))
115 std::vector<std::string> &path = impl_->searchPath_; // convenience
116 const std::string defaultPath = impl_->getDefaultPath();
117 std::vector<std::string> tmpPath;
118 Path::splitPathEnvironment(lib, &tmpPath);
119 std::set<std::string> pathsSeen;
120 pathsSeen.insert(defaultPath);
121 for (auto &d : tmpPath)
123 if (!pathsSeen.count(d))
125 path.push_back(d);
126 pathsSeen.insert(d);
129 impl_->bEnvIsSet_ = true;
133 FILE *DataFileFinder::openFile(const DataFileOptions &options) const
135 // TODO: There is a small race here, since there is some time between
136 // the exists() calls and actually opening the file. It would be better
137 // to leave the file open after a successful exists() if the desire is to
138 // actually open the file.
139 std::string filename = findFile(options);
140 if (filename.empty())
142 return nullptr;
144 #if 0
145 if (debug)
147 fprintf(debug, "Opening library file %s\n", fn);
149 #endif
150 return TextInputFile::openRawHandle(filename);
153 std::string DataFileFinder::findFile(const DataFileOptions &options) const
155 if (options.bCurrentDir_ && Path::exists(options.filename_))
157 return options.filename_;
159 if (impl_ != nullptr)
161 std::vector<std::string>::const_iterator i;
162 for (i = impl_->searchPath_.begin(); i != impl_->searchPath_.end(); ++i)
164 // TODO: Deal with an empty search path entry more reasonably.
165 std::string testPath = Path::join(*i, options.filename_);
166 // TODO: Consider skipping directories.
167 if (Path::exists(testPath))
169 return testPath;
173 const std::string &defaultPath = Impl::getDefaultPath();
174 if (!defaultPath.empty())
176 std::string testPath = Path::join(defaultPath, options.filename_);
177 if (Path::exists(testPath))
179 return testPath;
182 if (options.bThrow_)
184 const char *const envName = (impl_ != nullptr ? impl_->envName_ : nullptr);
185 const bool bEnvIsSet = (impl_ != nullptr ? impl_->bEnvIsSet_ : false);
186 std::string message(
187 formatString("Library file '%s' not found", options.filename_));
188 if (options.bCurrentDir_)
190 message.append(" in current dir nor");
192 if (bEnvIsSet)
194 message.append(formatString(" in your %s path nor", envName));
196 message.append(" in the default directories.\nThe following paths were searched:");
197 if (options.bCurrentDir_)
199 message.append("\n ");
200 message.append(Path::getWorkingDirectory());
201 message.append(" (current dir)");
203 if (impl_ != nullptr)
205 std::vector<std::string>::const_iterator i;
206 for (i = impl_->searchPath_.begin(); i != impl_->searchPath_.end(); ++i)
208 message.append("\n ");
209 message.append(*i);
212 if (!defaultPath.empty())
214 message.append("\n ");
215 message.append(defaultPath);
216 message.append(" (default)");
218 if (!bEnvIsSet && envName != nullptr)
220 message.append(
221 formatString("\nYou can set additional directories to search "
222 "with the %s path variable.", envName));
224 GMX_THROW(FileIOError(message));
226 return std::string();
229 std::vector<DataFileInfo>
230 DataFileFinder::enumerateFiles(const DataFileOptions &options) const
232 // TODO: Consider if not being able to list one of the directories should
233 // really be a fatal error. Or alternatively, check somewhere else that
234 // paths in GMXLIB are valid.
235 std::vector<DataFileInfo> result;
236 std::vector<std::string>::const_iterator i;
237 if (options.bCurrentDir_)
239 std::vector<std::string> files
240 = DirectoryEnumerator::enumerateFilesWithExtension(
241 ".", options.filename_, false);
242 for (i = files.begin(); i != files.end(); ++i)
244 result.emplace_back(".", *i, false);
247 if (impl_ != nullptr)
249 std::vector<std::string>::const_iterator j;
250 for (j = impl_->searchPath_.begin(); j != impl_->searchPath_.end(); ++j)
252 std::vector<std::string> files
253 = DirectoryEnumerator::enumerateFilesWithExtension(
254 j->c_str(), options.filename_, false);
255 for (i = files.begin(); i != files.end(); ++i)
257 result.emplace_back(*j, *i, false);
261 const std::string &defaultPath = Impl::getDefaultPath();
262 if (!defaultPath.empty())
264 std::vector<std::string> files
265 = DirectoryEnumerator::enumerateFilesWithExtension(
266 defaultPath.c_str(), options.filename_, false);
267 for (i = files.begin(); i != files.end(); ++i)
269 result.emplace_back(defaultPath, *i, true);
272 if (result.empty() && options.bThrow_)
274 // TODO: Print the search path as is done in findFile().
275 std::string message(
276 formatString("Could not find any files ending on '%s' in the "
277 "current directory or the GROMACS library search path",
278 options.filename_));
279 GMX_THROW(FileIOError(message));
281 return result;
284 } // namespace gmx