Remove gmx::File (except for File::exists())
[alexxy/gromacs.git] / src / gromacs / utility / datafilefinder.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements gmx::DataFileFinder.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_utility
41  */
42 #include "gmxpre.h"
43
44 #include "datafilefinder.h"
45
46 #include <cstdlib>
47
48 #include <string>
49 #include <vector>
50
51 #include "buildinfo.h"
52 #include "gromacs/utility/directoryenumerator.h"
53 #include "gromacs/utility/exceptions.h"
54 #include "gromacs/utility/filestream.h"
55 #include "gromacs/utility/path.h"
56 #include "gromacs/utility/programcontext.h"
57 #include "gromacs/utility/stringutil.h"
58
59 namespace gmx
60 {
61
62 /********************************************************************
63  * DataFileFinder::Impl
64  */
65
66 class DataFileFinder::Impl
67 {
68     public:
69         static std::string getDefaultPath();
70
71         Impl() : envName_(NULL), bEnvIsSet_(false) {}
72
73         const char               *envName_;
74         bool                      bEnvIsSet_;
75         std::vector<std::string>  searchPath_;
76 };
77
78 std::string DataFileFinder::Impl::getDefaultPath()
79 {
80     const InstallationPrefixInfo installPrefix
81         = getProgramContext().installationPrefix();
82     if (!isNullOrEmpty(installPrefix.path))
83     {
84         const char *const dataPath
85             = installPrefix.bSourceLayout ? "share" : DATA_INSTALL_DIR;
86         return Path::join(installPrefix.path, dataPath, "top");
87     }
88     return std::string();
89 }
90
91 /********************************************************************
92  * DataFileFinder
93  */
94
95 DataFileFinder::DataFileFinder()
96     : impl_(NULL)
97 {
98 }
99
100 DataFileFinder::~DataFileFinder()
101 {
102 }
103
104 void DataFileFinder::setSearchPathFromEnv(const char *envVarName)
105 {
106     if (!impl_.get())
107     {
108         impl_.reset(new Impl());
109     }
110     impl_->envName_ = envVarName;
111     const char *const lib = getenv(envVarName);
112     if (lib != NULL)
113     {
114         impl_->bEnvIsSet_ = true;
115         Path::splitPathEnvironment(lib, &impl_->searchPath_);
116     }
117 }
118
119 FILE *DataFileFinder::openFile(const DataFileOptions &options) const
120 {
121     // TODO: There is a small race here, since there is some time between
122     // the exists() calls and actually opening the file.  It would be better
123     // to leave the file open after a successful exists() if the desire is to
124     // actually open the file.
125     std::string filename = findFile(options);
126     if (filename.empty())
127     {
128         return NULL;
129     }
130 #if 0
131     if (debug)
132     {
133         fprintf(debug, "Opening library file %s\n", fn);
134     }
135 #endif
136     return TextInputFile::openRawHandle(filename);
137 }
138
139 std::string DataFileFinder::findFile(const DataFileOptions &options) const
140 {
141     if (options.bCurrentDir_ && Path::exists(options.filename_))
142     {
143         return options.filename_;
144     }
145     if (impl_.get())
146     {
147         std::vector<std::string>::const_iterator i;
148         for (i = impl_->searchPath_.begin(); i != impl_->searchPath_.end(); ++i)
149         {
150             // TODO: Deal with an empty search path entry more reasonably.
151             std::string testPath = Path::join(*i, options.filename_);
152             // TODO: Consider skipping directories.
153             if (Path::exists(testPath))
154             {
155                 return testPath;
156             }
157         }
158     }
159     const std::string &defaultPath = Impl::getDefaultPath();
160     if (!defaultPath.empty())
161     {
162         std::string testPath = Path::join(defaultPath, options.filename_);
163         if (Path::exists(testPath))
164         {
165             return testPath;
166         }
167     }
168     if (options.bThrow_)
169     {
170         const char *const envName   = (impl_.get() ? impl_->envName_ : NULL);
171         const bool        bEnvIsSet = (impl_.get() ? impl_->bEnvIsSet_ : false);
172         std::string       message(
173                 formatString("Library file '%s' not found", options.filename_));
174         if (options.bCurrentDir_)
175         {
176             message.append(" in current dir nor");
177         }
178         if (bEnvIsSet)
179         {
180             message.append(formatString(" in your %s path nor", envName));
181         }
182         message.append(" in the default directories.\nThe following paths were searched:");
183         if (options.bCurrentDir_)
184         {
185             message.append("\n  ");
186             message.append(Path::getWorkingDirectory());
187             message.append(" (current dir)");
188         }
189         if (impl_.get())
190         {
191             std::vector<std::string>::const_iterator i;
192             for (i = impl_->searchPath_.begin(); i != impl_->searchPath_.end(); ++i)
193             {
194                 message.append("\n  ");
195                 message.append(*i);
196             }
197         }
198         if (!defaultPath.empty())
199         {
200             message.append("\n  ");
201             message.append(defaultPath);
202             message.append(" (default)");
203         }
204         if (!bEnvIsSet && envName != NULL)
205         {
206             message.append(
207                     formatString("\nYou can set additional directories to search "
208                                  "with the %s path variable.", envName));
209         }
210         GMX_THROW(FileIOError(message));
211     }
212     return std::string();
213 }
214
215 std::vector<DataFileInfo>
216 DataFileFinder::enumerateFiles(const DataFileOptions &options) const
217 {
218     // TODO: Consider if not being able to list one of the directories should
219     // really be a fatal error. Or alternatively, check somewhere else that
220     // paths in GMXLIB are valid.
221     std::vector<DataFileInfo>                result;
222     std::vector<std::string>::const_iterator i;
223     if (options.bCurrentDir_)
224     {
225         std::vector<std::string> files
226             = DirectoryEnumerator::enumerateFilesWithExtension(
227                         ".", options.filename_, false);
228         for (i = files.begin(); i != files.end(); ++i)
229         {
230             result.push_back(DataFileInfo(".", *i, false));
231         }
232     }
233     if (impl_.get())
234     {
235         std::vector<std::string>::const_iterator j;
236         for (j = impl_->searchPath_.begin(); j != impl_->searchPath_.end(); ++j)
237         {
238             std::vector<std::string> files
239                 = DirectoryEnumerator::enumerateFilesWithExtension(
240                             j->c_str(), options.filename_, false);
241             for (i = files.begin(); i != files.end(); ++i)
242             {
243                 result.push_back(DataFileInfo(*j, *i, false));
244             }
245         }
246     }
247     const std::string &defaultPath = Impl::getDefaultPath();
248     if (!defaultPath.empty())
249     {
250         std::vector<std::string> files
251             = DirectoryEnumerator::enumerateFilesWithExtension(
252                         defaultPath.c_str(), options.filename_, false);
253         for (i = files.begin(); i != files.end(); ++i)
254         {
255             result.push_back(DataFileInfo(defaultPath, *i, true));
256         }
257     }
258     if (result.empty() && options.bThrow_)
259     {
260         // TODO: Print the search path as is done in findFile().
261         std::string message(
262                 formatString("Could not find any files ending on '%s' in the "
263                              "current directory or the GROMACS library search path",
264                              options.filename_));
265         GMX_THROW(FileIOError(message));
266     }
267     return result;
268 }
269
270 } // namespace gmx