Chaste  Release::3.4
FileFinder.cpp
1 /*
2 
3 Copyright (c) 2005-2016, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are met:
14  * Redistributions of source code must retain the above copyright notice,
15  this list of conditions and the following disclaimer.
16  * Redistributions in binary form must reproduce the above copyright notice,
17  this list of conditions and the following disclaimer in the documentation
18  and/or other materials provided with the distribution.
19  * Neither the name of the University of Oxford nor the names of its
20  contributors may be used to endorse or promote products derived from this
21  software without specific prior written permission.
22 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
36 #include "FileFinder.hpp"
37 
38 #include <cassert>
39 
40 #include "BoostFilesystem.hpp"
41 #include "ChasteBuildRoot.hpp"
42 #include "Exception.hpp"
43 #include "GetCurrentWorkingDirectory.hpp"
44 #include "OutputFileHandler.hpp"
45 #include "Warnings.hpp"
46 #include "PosixPathFixer.hpp"
47 
48 bool FileFinder::msFaking = false;
49 
51 
52 std::string FileFinder::msFakePath = "";
53 
54 #define UNSET_PATH "UNSET!"
55 
68 #define CONVERT_ERROR(code) \
69  try { \
70  code; \
71  } catch (const fs::filesystem_error& e) { \
72  EXCEPTION(e.what()); \
73  }
74 
76  : mAbsPath(UNSET_PATH)
77 {
78 }
79 
80 FileFinder::FileFinder(const std::string& rRelativePath, RelativeTo::Value relativeTo)
81 {
82  SetPath(rRelativePath, relativeTo);
83 }
84 
85 FileFinder::FileFinder(const std::string& rLeafName, const FileFinder& rParentOrSibling)
86 {
87  SetPath(rLeafName, rParentOrSibling);
88 }
89 
90 FileFinder::FileFinder(const fs::path& rPath)
91 {
92  SetPath(fs::complete(rPath).string(), RelativeTo::Absolute);
93 }
94 
96 {
97 }
98 
99 void FileFinder::SetPath(const std::string& rRelativePath, RelativeTo::Value relativeTo)
100 {
101  switch (relativeTo)
102  {
104  mAbsPath = ChasteSourceRootDir() + rRelativePath;
105  break;
106 
108  mAbsPath = ChasteBuildRootDir() + rRelativePath;
109  break;
110 
113  break;
114 
115  case RelativeTo::CWD:
116  mAbsPath = GetCurrentWorkingDirectory() + "/" + rRelativePath;
117  break;
118 
120  mAbsPath = rRelativePath;
121  break;
122 
124  if (FileFinder::IsAbsolutePath(rRelativePath))
125  {
126  mAbsPath = rRelativePath;
127  }
128  else
129  {
130  mAbsPath = GetCurrentWorkingDirectory() + "/" + rRelativePath;
131  }
132  break;
133 
134  default:
135  // Getting here is impossible
137  break;
138  }
139 
140  if (msFaking && msFakeWhat == relativeTo)
141  {
142  // Fake the resulting path
143  mAbsPath = msFakePath + "/" + rRelativePath;
144  }
145 
146  // Remove any trailing /
147  std::string::iterator it = mAbsPath.end();
148  while (it != mAbsPath.begin() && *(--it) == '/')
149  {
150  // Iterator was decremented in the while test
151  }
152  // it now points at the last non-slash character, if any
153  if (it != mAbsPath.end() && (++it) != mAbsPath.end())
154  {
155  mAbsPath.erase(it, mAbsPath.end());
156  }
157 }
158 
159 void FileFinder::SetPath(const std::string& rLeafName, const FileFinder& rParentOrSibling)
160 {
161  if (!rParentOrSibling.Exists())
162  {
163  EXCEPTION("Reference path '" << rParentOrSibling.GetAbsolutePath() << "' does not exist.");
164  }
165  if (rParentOrSibling.IsDir())
166  {
167  SetPath(rParentOrSibling.GetAbsolutePath() + rLeafName, RelativeTo::Absolute);
168  }
169  else
170  {
171  SetPath(rParentOrSibling.GetParent().GetAbsolutePath() + rLeafName, RelativeTo::Absolute);
172  }
173 }
174 
176 {
177  return mAbsPath != UNSET_PATH;
178 }
179 
180 bool FileFinder::Exists() const
181 {
182  return fs::exists(mAbsPath);
183 }
184 
185 bool FileFinder::IsFile() const
186 {
187  return fs::is_regular(mAbsPath);
188 }
189 
190 bool FileFinder::IsDir() const
191 {
192  return fs::is_directory(mAbsPath);
193 }
194 
196 {
197  bool empty = true;
198  if (IsFile())
199  {
200  empty = (fs::file_size(mAbsPath) == 0u);
201  }
202  else if (IsDir())
203  {
204  fs::directory_iterator end_iter;
205  for (fs::directory_iterator dir_iter(mAbsPath); dir_iter != end_iter; ++dir_iter)
206  {
207  if (PATH_LEAF_NAME(dir_iter->path()).substr(0, 1) != ".")
208  {
209  empty = false;
210  break;
211  }
212  }
213  }
214  else
215  {
216  EXCEPTION("The path '" << mAbsPath << "' does not exist.");
217  }
218  return empty;
219 }
220 
221 std::string FileFinder::GetAbsolutePath() const
222 {
223  if (IsDir())
224  {
225  return mAbsPath + '/';
226  }
227  return mAbsPath;
228 }
229 
230 bool FileFinder::IsNewerThan(const FileFinder& rOtherEntity) const
231 {
232  assert(Exists());
233  assert(rOtherEntity.Exists());
234  return fs::last_write_time(mAbsPath) > fs::last_write_time(rOtherEntity.mAbsPath);
235 }
236 
237 std::string FileFinder::GetLeafName() const
238 {
239  return PATH_LEAF_NAME(fs::path(mAbsPath));
240 }
241 
243 {
244  return fs::basename(mAbsPath);
245 }
246 
247 std::string FileFinder::GetExtension() const
248 {
249  return fs::extension(mAbsPath);
250 }
251 
253 {
254  fs::path our_path(mAbsPath);
255  EXCEPT_IF_NOT(our_path.has_branch_path());
256  return FileFinder(our_path.branch_path().string(),
258 }
259 
260 
261 std::string FileFinder::GetRelativePath(const FileFinder& rBasePath) const
262 {
263  const std::string base_path = rBasePath.GetAbsolutePath();
264  const std::string our_path = GetAbsolutePath();
265  if (our_path.substr(0, base_path.length()) != base_path)
266  {
267  EXCEPTION("The path '" << our_path << "' is not relative to '" << base_path << "'.");
268  }
269  return our_path.substr(base_path.length());
270 }
271 
272 
278 void RecursiveCopy(const fs::path& rFromPath, const fs::path& rToPath);
279 
280 void RecursiveCopy(const fs::path& rFromPath, const fs::path& rToPath)
281 {
282  fs::path dest = rToPath;
283  // If rToPath is a folder, then we're copying to the source name *inside* this folder
284  if (fs::is_directory(dest))
285  {
286  dest /= rFromPath.leaf();
287  }
288  // If the source is a folder, it's complicated
289  if (fs::is_directory(rFromPath))
290  {
291  // Create the destination folder
292  EXCEPT_IF_NOT(!fs::exists(dest));
293  fs::create_directory(dest);
294  // Recursively copy our contents
295  fs::directory_iterator end_iter;
296  for (fs::directory_iterator dir_iter(rFromPath); dir_iter != end_iter; ++dir_iter)
297  {
298  RecursiveCopy(dir_iter->path(), dest);
299  }
300  }
301  else
302  {
303  fs::copy_file(rFromPath, dest); // Just copy!
304  }
305 }
306 
307 
309 {
310  if (!Exists())
311  {
312  EXCEPTION("Cannot copy '" << mAbsPath << "' as it does not exist.");
313  }
314  fs::path from_path(mAbsPath);
315  fs::path to_path(rDest.mAbsPath);
316  if (rDest.IsDir())
317  {
318  to_path /= from_path.leaf();
319  }
320  if (fs::exists(to_path))
321  {
322  if (IsFile())
323  {
324  CONVERT_ERROR(fs::remove(to_path));
325  }
326  else
327  {
328  EXCEPTION("Cannot copy '" << mAbsPath << "' to '" << to_path << "' as it would overwrite an existing file.");
329  }
330  }
331  CONVERT_ERROR(RecursiveCopy(from_path, to_path));
332  return FileFinder(to_path);
333 }
334 
335 
340 void RemoveAll(const fs::path& rPath);
341 
342 void RemoveAll(const fs::path& rPath)
343 {
344  // First recursively remove any children
345  if (fs::is_directory(rPath))
346  {
347  fs::directory_iterator end_iter;
348  for (fs::directory_iterator dir_iter(rPath); dir_iter != end_iter; ++dir_iter)
349  {
350  RemoveAll(dir_iter->path());
351  }
352  }
353  // Now remove the item itself
354  fs::remove(rPath);
355 }
356 
357 
358 
359 void FileFinder::PrivateRemove(bool dangerous) const
360 {
361  // Test for bad paths
362  const std::string test_output(OutputFileHandler::GetChasteTestOutputDirectory());
363  const std::string test_output_path(ChastePosixPathFixer::ToPosix(fs::path(test_output)));
364  const std::string absolute_path(ChastePosixPathFixer::ToPosix(fs::path(GetAbsolutePath())));
365  bool in_testoutput = (absolute_path.substr(0, test_output_path.length()) == test_output_path);
366 
367  if (!in_testoutput)
368  {
369  if (dangerous)
370  {
371  const std::string source_folder(FileFinder("",RelativeTo::ChasteSourceRoot).GetAbsolutePath());
372  const std::string source_folder_path = ChastePosixPathFixer::ToPosix(fs::path(source_folder));
373  bool in_source = (absolute_path.substr(0, source_folder_path.length()) == source_folder_path);
374 
375  const std::string build_folder(FileFinder("",RelativeTo::ChasteBuildRoot).GetAbsolutePath());
376  const std::string build_folder_path = ChastePosixPathFixer::ToPosix(fs::path(build_folder));
377  bool in_build = (absolute_path.substr(0, build_folder_path.length()) == build_folder_path);
378 
379  if (!(in_source || in_build))
380  {
381  EXCEPTION("Cannot remove location '" << mAbsPath
382  << "' as it is not located within the Chaste test output folder ("
383  << test_output_path << "), the Chaste source folder ("
384  << source_folder_path <<") or the Chaste build folder ("
385  << build_folder_path <<").");
386  }
387  }
388  else
389  {
390  EXCEPTION("Cannot remove location '" << mAbsPath
391  << "' as it is not located within the Chaste test output folder ("
392  << test_output_path << ").");
393  }
394  }
395 
396  if (mAbsPath.find("..") != std::string::npos)
397  {
398  EXCEPTION("Cannot remove location '" << mAbsPath
399  << "' as it contains a dangerous path component.");
400  }
401  if (Exists())
402  {
403  if (!dangerous)
404  {
405  fs::path sig_file(mAbsPath);
406  if (IsFile())
407  {
408  // We need to look for the signature file in the parent folder
409  sig_file.remove_leaf();
410  }
412  if (!fs::exists(sig_file))
413  {
414  EXCEPTION("Cannot remove location '" << mAbsPath << "' because the signature file '"
415  << OutputFileHandler::SIG_FILE_NAME << "' is not present.");
416  }
417  }
418  // Do the removal
419  CONVERT_ERROR(RemoveAll(mAbsPath));
420  }
421 }
422 
423 void FileFinder::Remove() const
424 {
425  PrivateRemove();
426 }
427 
429 {
430  PrivateRemove(true);
431 }
432 
433 
434 std::vector<FileFinder> FileFinder::FindMatches(const std::string& rPattern) const
435 {
436  // Check for error/warning cases
437  if (!IsDir())
438  {
439  EXCEPTION("Cannot search for matching files in '" << mAbsPath << "' as it is not a directory.");
440  }
441  size_t len = rPattern.length();
442  size_t inner_star_pos = rPattern.find('*', 1);
443  if (inner_star_pos != std::string::npos && inner_star_pos < len - 1)
444  {
445  WARNING("A '*' only has special meaning at the start or end of a pattern.");
446  }
447 
448  // Note initial or trailing *, and use of ?
449  std::string pattern(rPattern);
450  bool star_fini = false;
451  if (!pattern.empty() && *(pattern.rbegin()) == '*')
452  {
453  star_fini = true;
454  pattern = pattern.substr(0, len-1);
455  len--;
456  }
457  bool star_init = false;
458  if (!pattern.empty() && pattern[0] == '*')
459  {
460  star_init = true;
461  pattern = pattern.substr(1);
462  len--;
463  }
464  bool has_query = (pattern.find('?') != std::string::npos);
465  // Disallow a harder case to match
466  if (star_init && star_fini && has_query)
467  {
468  EXCEPTION("The '*' wildcard may not be used at both the start and end of the pattern if the '?' wildcard is also used.");
469  }
470 
471  // Search the folder
472  std::vector<FileFinder> results;
473  if (!rPattern.empty())
474  {
475  fs::directory_iterator end_iter;
476  fs::path our_path(mAbsPath);
477  for (fs::directory_iterator dir_iter(our_path); dir_iter != end_iter; ++dir_iter)
478  {
479  std::string leafname = PATH_LEAF_NAME(dir_iter->path());
480  size_t leaf_len = leafname.length();
481  if (leafname[0] != '.' // Don't include hidden files
482  && leaf_len >= len) // Ignore stuff that can't match
483  {
484  if (!has_query) // Easier case
485  {
486  size_t pos = leafname.find(pattern);
487  if ((star_init || pos == 0) && (star_fini || pos + len == leaf_len))
488  {
489  results.push_back(FileFinder(our_path / leafname));
490  }
491  }
492  else
493  {
494  std::string match;
495  if (star_init)
496  {
497  // Match against last len chars
498  match = leafname.substr(leaf_len - len);
499  }
500  else
501  {
502  // Match against first len chars
503  match = leafname.substr(0, len);
504  }
505  bool ok = true;
506  for (std::string::const_iterator it_p=pattern.begin(), it_m=match.begin();
507  it_p != pattern.end();
508  ++it_p, ++it_m)
509  {
510  if (*it_p != '?' && *it_p != *it_m)
511  {
512  ok = false;
513  break;
514  }
515  }
516  if (ok)
517  {
518  results.push_back(FileFinder(our_path / leafname));
519  }
520  }
521  }
522  }
523  }
524  return results;
525 }
526 
527 
528 bool FileFinder::IsAbsolutePath(const std::string& rPath)
529 {
530  return fs::path(rPath).is_complete();
531 }
532 
534 {
535  for (std::string::iterator it = rPath.begin(); it != rPath.end(); ++it)
536  {
537  if (*it == ' ')
538  {
539  *it = '_';
540  }
541  }
542 }
543 
545 {
546  for (std::string::iterator it = rPath.begin(); it != rPath.end(); ++it)
547  {
548  if (*it == '_')
549  {
550  *it = ' ';
551  }
552  }
553 }
554 
555 void FileFinder::FakePath(RelativeTo::Value fakeWhat, const std::string& rFakePath)
556 {
557  msFakeWhat = fakeWhat;
558  msFakePath = rFakePath;
559  msFaking = true;
560 }
561 
563 {
564  msFaking = false;
565 }
std::string mAbsPath
Definition: FileFinder.hpp:297
void DangerousRemove() const
Definition: FileFinder.cpp:428
static const std::string SIG_FILE_NAME
void PrivateRemove(bool dangerous=false) const
Definition: FileFinder.cpp:359
virtual ~FileFinder()
Definition: FileFinder.cpp:95
static bool IsAbsolutePath(const std::string &rPath)
Definition: FileFinder.cpp:528
bool IsFile() const
Definition: FileFinder.cpp:185
const char * ChasteSourceRootDir()
std::string GetAbsolutePath() const
Definition: FileFinder.cpp:221
static bool msFaking
Definition: FileFinder.hpp:300
bool IsPathSet() const
Definition: FileFinder.cpp:175
#define EXCEPTION(message)
Definition: Exception.hpp:143
static void StopFaking()
Definition: FileFinder.cpp:562
FileFinder CopyTo(const FileFinder &rDest) const
Definition: FileFinder.cpp:308
static RelativeTo::Value msFakeWhat
Definition: FileFinder.hpp:303
bool IsEmpty() const
Definition: FileFinder.cpp:195
#define NEVER_REACHED
Definition: Exception.hpp:206
static std::string msFakePath
Definition: FileFinder.hpp:306
bool IsDir() const
Definition: FileFinder.cpp:190
std::string GetExtension() const
Definition: FileFinder.cpp:247
#define EXCEPT_IF_NOT(test)
Definition: Exception.hpp:158
#define PATH_LEAF_NAME(path)
static std::string ToPosix(const fs::path path)
std::vector< FileFinder > FindMatches(const std::string &rPattern) const
Definition: FileFinder.cpp:434
bool Exists() const
Definition: FileFinder.cpp:180
const char * ChasteBuildRootDir()
FileFinder GetParent() const
Definition: FileFinder.cpp:252
static void ReplaceUnderscoresWithSpaces(std::string &rPath)
Definition: FileFinder.cpp:544
std::string GetLeafNameNoExtension() const
Definition: FileFinder.cpp:242
virtual void SetPath(const std::string &rPath, RelativeTo::Value relativeTo)
Definition: FileFinder.cpp:99
static std::string GetChasteTestOutputDirectory()
std::string GetLeafName() const
Definition: FileFinder.cpp:237
static void FakePath(RelativeTo::Value fakeWhat, const std::string &rFakePath)
Definition: FileFinder.cpp:555
std::string GetRelativePath(const FileFinder &rBasePath) const
Definition: FileFinder.cpp:261
bool IsNewerThan(const FileFinder &rOtherEntity) const
Definition: FileFinder.cpp:230
static void ReplaceSpacesWithUnderscores(std::string &rPath)
Definition: FileFinder.cpp:533
void Remove() const
Definition: FileFinder.cpp:423