Update instructions in containers.rst
[gromacs.git] / src / gromacs / utility / datafilefinder.cpp
blob9afb8c36ef44d72c8b4016298888f9e3e9557c58
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.
5 * Copyright (c) 2019,2020, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
36 /*! \internal \file
37 * \brief
38 * Implements gmx::DataFileFinder.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_utility
43 #include "gmxpre.h"
45 #include "datafilefinder.h"
47 #include <cstdlib>
49 #include <set>
50 #include <string>
51 #include <vector>
53 #include "buildinfo.h"
54 #include "gromacs/utility/directoryenumerator.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "gromacs/utility/filestream.h"
57 #include "gromacs/utility/path.h"
58 #include "gromacs/utility/programcontext.h"
59 #include "gromacs/utility/stringutil.h"
61 namespace gmx
64 /********************************************************************
65 * DataFileFinder::Impl
68 class DataFileFinder::Impl
70 public:
71 static std::string getDefaultPath();
73 Impl() : envName_(nullptr), bEnvIsSet_(false) {}
75 const char* envName_;
76 bool bEnvIsSet_;
77 std::vector<std::string> searchPath_;
80 std::string DataFileFinder::Impl::getDefaultPath()
82 const InstallationPrefixInfo installPrefix = getProgramContext().installationPrefix();
83 if (!isNullOrEmpty(installPrefix.path))
85 const char* const dataPath = installPrefix.bSourceLayout ? "share" : GMX_INSTALL_GMXDATADIR;
86 return Path::join(installPrefix.path, dataPath, "top");
88 return std::string();
91 /********************************************************************
92 * DataFileFinder
95 DataFileFinder::DataFileFinder() : impl_(nullptr) {}
97 DataFileFinder::~DataFileFinder() {}
99 void DataFileFinder::setSearchPathFromEnv(const char* envVarName)
101 if (!impl_)
103 impl_.reset(new Impl());
105 impl_->envName_ = envVarName;
106 const char* const lib = getenv(envVarName);
107 if (!isNullOrEmpty(lib))
109 std::vector<std::string>& path = impl_->searchPath_; // convenience
110 const std::string defaultPath = impl_->getDefaultPath();
111 std::vector<std::string> tmpPath;
112 Path::splitPathEnvironment(lib, &tmpPath);
113 std::set<std::string> pathsSeen;
114 pathsSeen.insert(defaultPath);
115 for (auto& d : tmpPath)
117 if (!pathsSeen.count(d))
119 path.push_back(d);
120 pathsSeen.insert(d);
123 impl_->bEnvIsSet_ = true;
127 FilePtr DataFileFinder::openFile(const DataFileOptions& options) const
129 // TODO: There is a small race here, since there is some time between
130 // the exists() calls and actually opening the file. It would be better
131 // to leave the file open after a successful exists() if the desire is to
132 // actually open the file.
133 std::string filename = findFile(options);
134 if (filename.empty())
136 return nullptr;
138 #if 0
139 if (debug)
141 fprintf(debug, "Opening library file %s\n", fn);
143 #endif
144 return TextInputFile::openRawHandle(filename);
147 std::string DataFileFinder::findFile(const DataFileOptions& options) const
149 if (options.bCurrentDir_ && Path::exists(options.filename_))
151 return options.filename_;
153 if (impl_ != nullptr)
155 std::vector<std::string>::const_iterator i;
156 for (i = impl_->searchPath_.begin(); i != impl_->searchPath_.end(); ++i)
158 // TODO: Deal with an empty search path entry more reasonably.
159 std::string testPath = Path::join(*i, options.filename_);
160 // TODO: Consider skipping directories.
161 if (Path::exists(testPath))
163 return testPath;
167 const std::string& defaultPath = Impl::getDefaultPath();
168 if (!defaultPath.empty())
170 std::string testPath = Path::join(defaultPath, options.filename_);
171 if (Path::exists(testPath))
173 return testPath;
176 if (options.bThrow_)
178 const char* const envName = (impl_ != nullptr ? impl_->envName_ : nullptr);
179 const bool bEnvIsSet = (impl_ != nullptr ? impl_->bEnvIsSet_ : false);
180 std::string message(formatString("Library file '%s' not found", options.filename_));
181 if (options.bCurrentDir_)
183 message.append(" in current dir nor");
185 if (bEnvIsSet)
187 message.append(formatString(" in your %s path nor", envName));
189 message.append(" in the default directories.\nThe following paths were searched:");
190 if (options.bCurrentDir_)
192 message.append("\n ");
193 message.append(Path::getWorkingDirectory());
194 message.append(" (current dir)");
196 if (impl_ != nullptr)
198 std::vector<std::string>::const_iterator i;
199 for (i = impl_->searchPath_.begin(); i != impl_->searchPath_.end(); ++i)
201 message.append("\n ");
202 message.append(*i);
205 if (!defaultPath.empty())
207 message.append("\n ");
208 message.append(defaultPath);
209 message.append(" (default)");
211 if (!bEnvIsSet && envName != nullptr)
213 message.append(
214 formatString("\nYou can set additional directories to search "
215 "with the %s path variable.",
216 envName));
218 GMX_THROW(FileIOError(message));
220 return std::string();
223 std::vector<DataFileInfo> DataFileFinder::enumerateFiles(const DataFileOptions& options) const
225 // TODO: Consider if not being able to list one of the directories should
226 // really be a fatal error. Or alternatively, check somewhere else that
227 // paths in GMXLIB are valid.
228 std::vector<DataFileInfo> result;
229 std::vector<std::string>::const_iterator i;
230 if (options.bCurrentDir_)
232 std::vector<std::string> files =
233 DirectoryEnumerator::enumerateFilesWithExtension(".", options.filename_, false);
234 for (i = files.begin(); i != files.end(); ++i)
236 result.emplace_back(".", *i, false);
239 if (impl_ != nullptr)
241 std::vector<std::string>::const_iterator j;
242 for (j = impl_->searchPath_.begin(); j != impl_->searchPath_.end(); ++j)
244 std::vector<std::string> files = DirectoryEnumerator::enumerateFilesWithExtension(
245 j->c_str(), options.filename_, false);
246 for (i = files.begin(); i != files.end(); ++i)
248 result.emplace_back(*j, *i, false);
252 const std::string& defaultPath = Impl::getDefaultPath();
253 if (!defaultPath.empty())
255 std::vector<std::string> files = DirectoryEnumerator::enumerateFilesWithExtension(
256 defaultPath.c_str(), options.filename_, false);
257 for (i = files.begin(); i != files.end(); ++i)
259 result.emplace_back(defaultPath, *i, true);
262 if (result.empty() && options.bThrow_)
264 // TODO: Print the search path as is done in findFile().
265 std::string message(
266 formatString("Could not find any files ending on '%s' in the "
267 "current directory or the GROMACS library search path",
268 options.filename_));
269 GMX_THROW(FileIOError(message));
271 return result;
274 } // namespace gmx