2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 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::DataFileFinder.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_utility
44 #include "datafilefinder.h"
51 #include "gromacs/utility/directoryenumerator.h"
52 #include "gromacs/utility/exceptions.h"
53 #include "gromacs/utility/file.h"
54 #include "gromacs/utility/path.h"
55 #include "gromacs/utility/programcontext.h"
56 #include "gromacs/utility/stringutil.h"
61 /********************************************************************
62 * DataFileFinder::Impl
65 class DataFileFinder::Impl
68 Impl() : envName_(NULL), bEnvIsSet_(false) {}
72 std::vector<std::string> searchPath_;
75 /********************************************************************
79 DataFileFinder::DataFileFinder()
84 DataFileFinder::~DataFileFinder()
88 void DataFileFinder::setSearchPathFromEnv(const char *envVarName)
92 impl_.reset(new Impl());
94 impl_->envName_ = envVarName;
95 const char *const lib = getenv(envVarName);
98 impl_->bEnvIsSet_ = true;
99 Path::splitPathEnvironment(lib, &impl_->searchPath_);
103 FILE *DataFileFinder::openFile(const DataFileOptions &options) const
105 // TODO: There is a small race here, since there is some time between
106 // the exists() calls and actually opening the file. It would be better
107 // to leave the file open after a successful exists() if the desire is to
108 // actually open the file.
109 std::string filename = findFile(options);
110 if (filename.empty())
117 fprintf(debug, "Opening library file %s\n", fn);
120 return File::openRawHandle(filename, "r");
123 std::string DataFileFinder::findFile(const DataFileOptions &options) const
125 if (options.bCurrentDir_ && Path::exists(options.filename_))
127 return options.filename_;
131 std::vector<std::string>::const_iterator i;
132 for (i = impl_->searchPath_.begin(); i != impl_->searchPath_.end(); ++i)
134 // TODO: Deal with an empty search path entry more reasonably.
135 std::string testPath = Path::join(*i, options.filename_);
136 // TODO: Consider skipping directories.
137 if (Path::exists(testPath))
143 const char *const defaultPath = getProgramContext().defaultLibraryDataPath();
144 if (defaultPath != NULL && defaultPath[0] != '\0')
146 std::string testPath = Path::join(defaultPath, options.filename_);
147 if (Path::exists(testPath))
154 const char *const envName = (impl_.get() ? impl_->envName_ : NULL);
155 const bool bEnvIsSet = (impl_.get() ? impl_->bEnvIsSet_ : false);
157 formatString("Library file '%s' not found", options.filename_));
158 if (options.bCurrentDir_)
160 message.append(" in current dir nor");
164 message.append(formatString(" in your %s path nor", envName));
166 message.append(" in the default directories.\nThe following paths were searched:");
167 if (options.bCurrentDir_)
169 message.append("\n ");
170 message.append(Path::getWorkingDirectory());
171 message.append(" (current dir)");
175 std::vector<std::string>::const_iterator i;
176 for (i = impl_->searchPath_.begin(); i != impl_->searchPath_.end(); ++i)
178 message.append("\n ");
182 if (defaultPath != NULL && defaultPath[0] != '\0')
184 message.append("\n ");
185 message.append(defaultPath);
186 message.append(" (default)");
188 if (!bEnvIsSet && envName != NULL)
191 formatString("\nYou can set additional directories to search "
192 "with the %s path variable.", envName));
194 GMX_THROW(FileIOError(message));
196 return std::string();
199 std::vector<DataFileInfo>
200 DataFileFinder::enumerateFiles(const DataFileOptions &options) const
202 // TODO: Consider if not being able to list one of the directories should
203 // really be a fatal error. Or alternatively, check somewhere else that
204 // paths in GMXLIB are valid.
205 std::vector<DataFileInfo> result;
206 std::vector<std::string>::const_iterator i;
207 if (options.bCurrentDir_)
209 std::vector<std::string> files
210 = DirectoryEnumerator::enumerateFilesWithExtension(
211 ".", options.filename_, false);
212 for (i = files.begin(); i != files.end(); ++i)
214 result.push_back(DataFileInfo(".", *i, false));
219 std::vector<std::string>::const_iterator j;
220 for (j = impl_->searchPath_.begin(); j != impl_->searchPath_.end(); ++j)
222 std::vector<std::string> files
223 = DirectoryEnumerator::enumerateFilesWithExtension(
224 j->c_str(), options.filename_, false);
225 for (i = files.begin(); i != files.end(); ++i)
227 result.push_back(DataFileInfo(*j, *i, false));
231 const char *const defaultPath = getProgramContext().defaultLibraryDataPath();
232 if (defaultPath != NULL && defaultPath[0] != '\0')
234 std::vector<std::string> files
235 = DirectoryEnumerator::enumerateFilesWithExtension(
236 defaultPath, options.filename_, false);
237 for (i = files.begin(); i != files.end(); ++i)
239 result.push_back(DataFileInfo(defaultPath, *i, true));
242 if (result.empty() && options.bThrow_)
244 // TODO: Print the search path as is done in findFile().
246 formatString("Could not find any files ending on '%s' in the "
247 "current directory or the GROMACS library search path",
249 GMX_THROW(FileIOError(message));