FileFinder.cpp

00001 /*
00002 
00003 Copyright (c) 2005-2015, University of Oxford.
00004 All rights reserved.
00005 
00006 University of Oxford means the Chancellor, Masters and Scholars of the
00007 University of Oxford, having an administrative office at Wellington
00008 Square, Oxford OX1 2JD, UK.
00009 
00010 This file is part of Chaste.
00011 
00012 Redistribution and use in source and binary forms, with or without
00013 modification, are permitted provided that the following conditions are met:
00014  * Redistributions of source code must retain the above copyright notice,
00015    this list of conditions and the following disclaimer.
00016  * Redistributions in binary form must reproduce the above copyright notice,
00017    this list of conditions and the following disclaimer in the documentation
00018    and/or other materials provided with the distribution.
00019  * Neither the name of the University of Oxford nor the names of its
00020    contributors may be used to endorse or promote products derived from this
00021    software without specific prior written permission.
00022 
00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00024 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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             // Getting here is impossible
00132             NEVER_REACHED;
00133             break;
00134     }
00135 
00136     if (msFaking && msFakeWhat == relativeTo)
00137     {
00138         // Fake the resulting path
00139         mAbsPath = msFakePath + "/" + rRelativePath;
00140     }
00141 
00142     // Remove any trailing /
00143     std::string::iterator it = mAbsPath.end();
00144     while (it != mAbsPath.begin() && *(--it) == '/')
00145     {
00146         // Iterator was decremented in the while test
00147     }
00148     // it now points at the last non-slash character, if any
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     // If rToPath is a folder, then we're copying to the source name *inside* this folder
00280     if (fs::is_directory(dest))
00281     {
00282         dest /= rFromPath.leaf();
00283     }
00284     // If the source is a folder, it's complicated
00285     if (fs::is_directory(rFromPath))
00286     {
00287         // Create the destination folder
00288         EXCEPT_IF_NOT(!fs::exists(dest));
00289         fs::create_directory(dest);
00290         // Recursively copy our contents
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); // Just copy!
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     // First recursively remove any children
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     // Now remove the item itself
00350     fs::remove(rPath);
00351 }
00352 
00353 
00354 
00355 void FileFinder::PrivateRemove(bool dangerous) const
00356 {
00357     // Test for bad paths
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                 // We need to look for the signature file in the parent folder
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         // Do the removal
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     // Check for error/warning cases
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     // Note initial or trailing *, and use of ?
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     // Disallow a harder case to match
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     // Search the folder
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] != '.'  // Don't include hidden files
00471                 && leaf_len >= len) // Ignore stuff that can't match
00472             {
00473                 if (!has_query) // Easier case
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                         // Match against last len chars
00487                         match = leafname.substr(leaf_len - len);
00488                     }
00489                     else
00490                     {
00491                         // Match against first len chars
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 }

Generated by  doxygen 1.6.2