FileFinder.cpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "FileFinder.hpp"
00037
00038 #include <cassert>
00039
00040 #include "BoostFilesystem.hpp"
00041 #include "ChasteBuildRoot.hpp"
00042 #include "Exception.hpp"
00043 #include "GetCurrentWorkingDirectory.hpp"
00044 #include "OutputFileHandler.hpp"
00045 #include "Warnings.hpp"
00046 #include "PosixPathFixer.hpp"
00047
00048 bool FileFinder::msFaking = false;
00049
00050 RelativeTo::Value FileFinder::msFakeWhat = RelativeTo::Absolute;
00051
00052 std::string FileFinder::msFakePath = "";
00053
00054 #define UNSET_PATH "UNSET!"
00055
00068 #define CONVERT_ERROR(code) \
00069 try { \
00070 code; \
00071 } catch (const fs::filesystem_error& e) { \
00072 EXCEPTION(e.what()); \
00073 }
00074
00075 FileFinder::FileFinder()
00076 : mAbsPath(UNSET_PATH)
00077 {
00078 }
00079
00080 FileFinder::FileFinder(const std::string& rRelativePath, RelativeTo::Value relativeTo)
00081 {
00082 SetPath(rRelativePath, relativeTo);
00083 }
00084
00085 FileFinder::FileFinder(const std::string& rLeafName, const FileFinder& rParentOrSibling)
00086 {
00087 SetPath(rLeafName, rParentOrSibling);
00088 }
00089
00090 FileFinder::FileFinder(const fs::path& rPath)
00091 {
00092 SetPath(fs::complete(rPath).string(), RelativeTo::Absolute);
00093 }
00094
00095 FileFinder::~FileFinder()
00096 {
00097 }
00098
00099 void FileFinder::SetPath(const std::string& rRelativePath, RelativeTo::Value relativeTo)
00100 {
00101 switch (relativeTo)
00102 {
00103 case RelativeTo::ChasteSourceRoot:
00104 mAbsPath = ChasteBuildRootDir() + rRelativePath;
00105 break;
00106
00107 case RelativeTo::ChasteTestOutput:
00108 mAbsPath = OutputFileHandler::GetChasteTestOutputDirectory() + rRelativePath;
00109 break;
00110
00111 case RelativeTo::CWD:
00112 mAbsPath = GetCurrentWorkingDirectory() + "/" + rRelativePath;
00113 break;
00114
00115 case RelativeTo::Absolute:
00116 mAbsPath = rRelativePath;
00117 break;
00118
00119 case RelativeTo::AbsoluteOrCwd:
00120 if (FileFinder::IsAbsolutePath(rRelativePath))
00121 {
00122 mAbsPath = rRelativePath;
00123 }
00124 else
00125 {
00126 mAbsPath = GetCurrentWorkingDirectory() + "/" + rRelativePath;
00127 }
00128 break;
00129
00130 default:
00131
00132 NEVER_REACHED;
00133 break;
00134 }
00135
00136 if (msFaking && msFakeWhat == relativeTo)
00137 {
00138
00139 mAbsPath = msFakePath + "/" + rRelativePath;
00140 }
00141
00142
00143 std::string::iterator it = mAbsPath.end();
00144 while (it != mAbsPath.begin() && *(--it) == '/')
00145 {
00146
00147 }
00148
00149 if (it != mAbsPath.end() && (++it) != mAbsPath.end())
00150 {
00151 mAbsPath.erase(it, mAbsPath.end());
00152 }
00153 }
00154
00155 void FileFinder::SetPath(const std::string& rLeafName, const FileFinder& rParentOrSibling)
00156 {
00157 if (!rParentOrSibling.Exists())
00158 {
00159 EXCEPTION("Reference path '" << rParentOrSibling.GetAbsolutePath() << "' does not exist.");
00160 }
00161 if (rParentOrSibling.IsDir())
00162 {
00163 SetPath(rParentOrSibling.GetAbsolutePath() + rLeafName, RelativeTo::Absolute);
00164 }
00165 else
00166 {
00167 SetPath(rParentOrSibling.GetParent().GetAbsolutePath() + rLeafName, RelativeTo::Absolute);
00168 }
00169 }
00170
00171 bool FileFinder::IsPathSet() const
00172 {
00173 return mAbsPath != UNSET_PATH;
00174 }
00175
00176 bool FileFinder::Exists() const
00177 {
00178 return fs::exists(mAbsPath);
00179 }
00180
00181 bool FileFinder::IsFile() const
00182 {
00183 return fs::is_regular(mAbsPath);
00184 }
00185
00186 bool FileFinder::IsDir() const
00187 {
00188 return fs::is_directory(mAbsPath);
00189 }
00190
00191 bool FileFinder::IsEmpty() const
00192 {
00193 bool empty = true;
00194 if (IsFile())
00195 {
00196 empty = (fs::file_size(mAbsPath) == 0u);
00197 }
00198 else if (IsDir())
00199 {
00200 fs::directory_iterator end_iter;
00201 for (fs::directory_iterator dir_iter(mAbsPath); dir_iter != end_iter; ++dir_iter)
00202 {
00203 if (PATH_LEAF_NAME(dir_iter->path()).substr(0, 1) != ".")
00204 {
00205 empty = false;
00206 break;
00207 }
00208 }
00209 }
00210 else
00211 {
00212 EXCEPTION("The path '" << mAbsPath << "' does not exist.");
00213 }
00214 return empty;
00215 }
00216
00217 std::string FileFinder::GetAbsolutePath() const
00218 {
00219 if (IsDir())
00220 {
00221 return mAbsPath + '/';
00222 }
00223 return mAbsPath;
00224 }
00225
00226 bool FileFinder::IsNewerThan(const FileFinder& rOtherEntity) const
00227 {
00228 assert(Exists());
00229 assert(rOtherEntity.Exists());
00230 return fs::last_write_time(mAbsPath) > fs::last_write_time(rOtherEntity.mAbsPath);
00231 }
00232
00233 std::string FileFinder::GetLeafName() const
00234 {
00235 return PATH_LEAF_NAME(fs::path(mAbsPath));
00236 }
00237
00238 std::string FileFinder::GetLeafNameNoExtension() const
00239 {
00240 return fs::basename(mAbsPath);
00241 }
00242
00243 std::string FileFinder::GetExtension() const
00244 {
00245 return fs::extension(mAbsPath);
00246 }
00247
00248 FileFinder FileFinder::GetParent() const
00249 {
00250 fs::path our_path(mAbsPath);
00251 EXCEPT_IF_NOT(our_path.has_branch_path());
00252 return FileFinder(our_path.branch_path().string(),
00253 RelativeTo::Absolute);
00254 }
00255
00256
00257 std::string FileFinder::GetRelativePath(const FileFinder& rBasePath) const
00258 {
00259 const std::string base_path = rBasePath.GetAbsolutePath();
00260 const std::string our_path = GetAbsolutePath();
00261 if (our_path.substr(0, base_path.length()) != base_path)
00262 {
00263 EXCEPTION("The path '" << our_path << "' is not relative to '" << base_path << "'.");
00264 }
00265 return our_path.substr(base_path.length());
00266 }
00267
00268
00274 void RecursiveCopy(const fs::path& rFromPath, const fs::path& rToPath);
00275
00276 void RecursiveCopy(const fs::path& rFromPath, const fs::path& rToPath)
00277 {
00278 fs::path dest = rToPath;
00279
00280 if (fs::is_directory(dest))
00281 {
00282 dest /= rFromPath.leaf();
00283 }
00284
00285 if (fs::is_directory(rFromPath))
00286 {
00287
00288 EXCEPT_IF_NOT(!fs::exists(dest));
00289 fs::create_directory(dest);
00290
00291 fs::directory_iterator end_iter;
00292 for (fs::directory_iterator dir_iter(rFromPath); dir_iter != end_iter; ++dir_iter)
00293 {
00294 RecursiveCopy(dir_iter->path(), dest);
00295 }
00296 }
00297 else
00298 {
00299 fs::copy_file(rFromPath, dest);
00300 }
00301 }
00302
00303
00304 FileFinder FileFinder::CopyTo(const FileFinder& rDest) const
00305 {
00306 if (!Exists())
00307 {
00308 EXCEPTION("Cannot copy '" << mAbsPath << "' as it does not exist.");
00309 }
00310 fs::path from_path(mAbsPath);
00311 fs::path to_path(rDest.mAbsPath);
00312 if (rDest.IsDir())
00313 {
00314 to_path /= from_path.leaf();
00315 }
00316 if (fs::exists(to_path))
00317 {
00318 if (IsFile())
00319 {
00320 CONVERT_ERROR(fs::remove(to_path));
00321 }
00322 else
00323 {
00324 EXCEPTION("Cannot copy '" << mAbsPath << "' to '" << to_path << "' as it would overwrite an existing file.");
00325 }
00326 }
00327 CONVERT_ERROR(RecursiveCopy(from_path, to_path));
00328 return FileFinder(to_path);
00329 }
00330
00331
00336 void RemoveAll(const fs::path& rPath);
00337
00338 void RemoveAll(const fs::path& rPath)
00339 {
00340
00341 if (fs::is_directory(rPath))
00342 {
00343 fs::directory_iterator end_iter;
00344 for (fs::directory_iterator dir_iter(rPath); dir_iter != end_iter; ++dir_iter)
00345 {
00346 RemoveAll(dir_iter->path());
00347 }
00348 }
00349
00350 fs::remove(rPath);
00351 }
00352
00353
00354
00355 void FileFinder::PrivateRemove(bool dangerous) const
00356 {
00357
00358 const std::string test_output(OutputFileHandler::GetChasteTestOutputDirectory());
00359 const std::string test_output_path(ChastePosixPathFixer::ToPosix(fs::path(test_output)));
00360 const std::string absolute_path(ChastePosixPathFixer::ToPosix(fs::path(GetAbsolutePath())));
00361 bool in_testoutput = (absolute_path.substr(0, test_output_path.length()) == test_output_path);
00362
00363 if (!in_testoutput)
00364 {
00365 if (dangerous)
00366 {
00367 const std::string source_folder(FileFinder("",RelativeTo::ChasteSourceRoot).GetAbsolutePath());
00368 const std::string source_folder_path = ChastePosixPathFixer::ToPosix(fs::path(source_folder));
00369 bool in_source = (absolute_path.substr(0, source_folder_path.length()) == source_folder_path);
00370 if (!in_source)
00371 {
00372 EXCEPTION("Cannot remove location '" << mAbsPath
00373 << "' as it is not located within the Chaste test output folder ("
00374 << test_output_path << ") or the Chaste source folder (" << source_folder_path <<").");
00375 }
00376 }
00377 else
00378 {
00379 EXCEPTION("Cannot remove location '" << mAbsPath
00380 << "' as it is not located within the Chaste test output folder ("
00381 << test_output_path << ").");
00382 }
00383 }
00384
00385 if (mAbsPath.find("..") != std::string::npos)
00386 {
00387 EXCEPTION("Cannot remove location '" << mAbsPath
00388 << "' as it contains a dangerous path component.");
00389 }
00390 if (Exists())
00391 {
00392 if (!dangerous)
00393 {
00394 fs::path sig_file(mAbsPath);
00395 if (IsFile())
00396 {
00397
00398 sig_file.remove_leaf();
00399 }
00400 sig_file /= OutputFileHandler::SIG_FILE_NAME;
00401 if (!fs::exists(sig_file))
00402 {
00403 EXCEPTION("Cannot remove location '" << mAbsPath << "' because the signature file '"
00404 << OutputFileHandler::SIG_FILE_NAME << "' is not present.");
00405 }
00406 }
00407
00408 CONVERT_ERROR(RemoveAll(mAbsPath));
00409 }
00410 }
00411
00412 void FileFinder::Remove() const
00413 {
00414 PrivateRemove();
00415 }
00416
00417 void FileFinder::DangerousRemove() const
00418 {
00419 PrivateRemove(true);
00420 }
00421
00422
00423 std::vector<FileFinder> FileFinder::FindMatches(const std::string& rPattern) const
00424 {
00425
00426 if (!IsDir())
00427 {
00428 EXCEPTION("Cannot search for matching files in '" << mAbsPath << "' as it is not a directory.");
00429 }
00430 size_t len = rPattern.length();
00431 size_t inner_star_pos = rPattern.find('*', 1);
00432 if (inner_star_pos != std::string::npos && inner_star_pos < len - 1)
00433 {
00434 WARNING("A '*' only has special meaning at the start or end of a pattern.");
00435 }
00436
00437
00438 std::string pattern(rPattern);
00439 bool star_fini = false;
00440 if (!pattern.empty() && *(pattern.rbegin()) == '*')
00441 {
00442 star_fini = true;
00443 pattern = pattern.substr(0, len-1);
00444 len--;
00445 }
00446 bool star_init = false;
00447 if (!pattern.empty() && pattern[0] == '*')
00448 {
00449 star_init = true;
00450 pattern = pattern.substr(1);
00451 len--;
00452 }
00453 bool has_query = (pattern.find('?') != std::string::npos);
00454
00455 if (star_init && star_fini && has_query)
00456 {
00457 EXCEPTION("The '*' wildcard may not be used at both the start and end of the pattern if the '?' wildcard is also used.");
00458 }
00459
00460
00461 std::vector<FileFinder> results;
00462 if (!rPattern.empty())
00463 {
00464 fs::directory_iterator end_iter;
00465 fs::path our_path(mAbsPath);
00466 for (fs::directory_iterator dir_iter(our_path); dir_iter != end_iter; ++dir_iter)
00467 {
00468 std::string leafname = PATH_LEAF_NAME(dir_iter->path());
00469 size_t leaf_len = leafname.length();
00470 if (leafname[0] != '.'
00471 && leaf_len >= len)
00472 {
00473 if (!has_query)
00474 {
00475 size_t pos = leafname.find(pattern);
00476 if ((star_init || pos == 0) && (star_fini || pos + len == leaf_len))
00477 {
00478 results.push_back(FileFinder(our_path / leafname));
00479 }
00480 }
00481 else
00482 {
00483 std::string match;
00484 if (star_init)
00485 {
00486
00487 match = leafname.substr(leaf_len - len);
00488 }
00489 else
00490 {
00491
00492 match = leafname.substr(0, len);
00493 }
00494 bool ok = true;
00495 for (std::string::const_iterator it_p=pattern.begin(), it_m=match.begin();
00496 it_p != pattern.end();
00497 ++it_p, ++it_m)
00498 {
00499 if (*it_p != '?' && *it_p != *it_m)
00500 {
00501 ok = false;
00502 break;
00503 }
00504 }
00505 if (ok)
00506 {
00507 results.push_back(FileFinder(our_path / leafname));
00508 }
00509 }
00510 }
00511 }
00512 }
00513 return results;
00514 }
00515
00516
00517 bool FileFinder::IsAbsolutePath(const std::string& rPath)
00518 {
00519 return fs::path(rPath).is_complete();
00520 }
00521
00522 void FileFinder::ReplaceSpacesWithUnderscores(std::string& rPath)
00523 {
00524 for (std::string::iterator it = rPath.begin(); it != rPath.end(); ++it)
00525 {
00526 if (*it == ' ')
00527 {
00528 *it = '_';
00529 }
00530 }
00531 }
00532
00533 void FileFinder::ReplaceUnderscoresWithSpaces(std::string& rPath)
00534 {
00535 for (std::string::iterator it = rPath.begin(); it != rPath.end(); ++it)
00536 {
00537 if (*it == '_')
00538 {
00539 *it = ' ';
00540 }
00541 }
00542 }
00543
00544 void FileFinder::FakePath(RelativeTo::Value fakeWhat, const std::string& rFakePath)
00545 {
00546 msFakeWhat = fakeWhat;
00547 msFakePath = rFakePath;
00548 msFaking = true;
00549 }
00550
00551 void FileFinder::StopFaking()
00552 {
00553 msFaking = false;
00554 }