Chaste Release::3.1
FileFinder.cpp
00001 /*
00002 
00003 Copyright (c) 2005-2012, 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 
00047 bool FileFinder::msFaking = false;
00048 
00049 RelativeTo::Value FileFinder::msFakeWhat = RelativeTo::Absolute;
00050 
00051 std::string FileFinder::msFakePath = "";
00052 
00053 #define UNSET_PATH "UNSET!"
00054 
00055 
00056 FileFinder::FileFinder()
00057     : mAbsPath(UNSET_PATH)
00058 {
00059 }
00060 
00061 FileFinder::FileFinder(const std::string& rRelativePath, RelativeTo::Value relativeTo)
00062 {
00063     SetPath(rRelativePath, relativeTo);
00064 }
00065 
00066 FileFinder::FileFinder(const std::string& rLeafName, const FileFinder& rParentOrSibling)
00067 {
00068     SetPath(rLeafName, rParentOrSibling);
00069 }
00070 
00071 FileFinder::FileFinder(const fs::path& rPath)
00072 {
00073     SetPath(fs::complete(rPath).string(), RelativeTo::Absolute);
00074 }
00075 
00076 void FileFinder::SetPath(const std::string& rRelativePath, RelativeTo::Value relativeTo)
00077 {
00078     switch (relativeTo)
00079     {
00080         case RelativeTo::ChasteSourceRoot:
00081             mAbsPath = ChasteBuildRootDir() + rRelativePath;
00082             break;
00083 
00084         case RelativeTo::ChasteTestOutput:
00085             mAbsPath = OutputFileHandler::GetChasteTestOutputDirectory() + rRelativePath;
00086             break;
00087 
00088         case RelativeTo::CWD:
00089             mAbsPath = GetCurrentWorkingDirectory() + "/" + rRelativePath;
00090             break;
00091 
00092         case RelativeTo::Absolute:
00093             mAbsPath = rRelativePath;
00094             break;
00095 
00096         case RelativeTo::AbsoluteOrCwd:
00097             if (FileFinder::IsAbsolutePath(rRelativePath))
00098             {
00099                 mAbsPath = rRelativePath;
00100             }
00101             else
00102             {
00103                 mAbsPath = GetCurrentWorkingDirectory() + "/" + rRelativePath;
00104             }
00105             break;
00106 
00107         default:
00108             // Getting here is impossible
00109             NEVER_REACHED;
00110             break;
00111     }
00112 
00113     if (msFaking && msFakeWhat == relativeTo)
00114     {
00115         // Fake the resulting path
00116         mAbsPath = msFakePath + "/" + rRelativePath;
00117     }
00118 
00119     // Remove any trailing /
00120     std::string::iterator it = mAbsPath.end();
00121     while (it != mAbsPath.begin() && *(--it) == '/')
00122     {
00123         // Iterator was decremented in the while test
00124     }
00125     if (it != mAbsPath.end() && (++it) != mAbsPath.end())
00126     {
00127         mAbsPath.erase(it, mAbsPath.end());
00128     }
00129 }
00130 
00131 bool FileFinder::IsPathSet() const
00132 {
00133     return mAbsPath != UNSET_PATH;
00134 }
00135 
00136 void FileFinder::SetPath(const std::string& rLeafName, const FileFinder& rParentOrSibling)
00137 {
00138     if (!rParentOrSibling.Exists())
00139     {
00140         EXCEPTION("Reference path '" << rParentOrSibling.GetAbsolutePath() << "' does not exist.");
00141     }
00142     if (rParentOrSibling.IsDir())
00143     {
00144         SetPath(rParentOrSibling.GetAbsolutePath() + rLeafName, RelativeTo::Absolute);
00145     }
00146     else
00147     {
00148         SetPath(rParentOrSibling.GetParent().GetAbsolutePath() + rLeafName, RelativeTo::Absolute);
00149     }
00150 }
00151 
00152 bool FileFinder::Exists() const
00153 {
00154     return fs::exists(mAbsPath);
00155 }
00156 
00157 bool FileFinder::IsFile() const
00158 {
00159     return fs::is_regular(mAbsPath);
00160 }
00161 
00162 bool FileFinder::IsDir() const
00163 {
00164     return fs::is_directory(mAbsPath);
00165 }
00166 
00167 bool FileFinder::IsEmpty() const
00168 {
00169     bool empty = true;
00170     if (IsFile())
00171     {
00172         empty = (fs::file_size(mAbsPath) == 0u);
00173     }
00174     else if (IsDir())
00175     {
00176         fs::directory_iterator end_iter;
00177         for (fs::directory_iterator dir_iter(mAbsPath); dir_iter != end_iter; ++dir_iter)
00178         {
00179             if (dir_iter->path().leaf().substr(0, 1) != ".")
00180             {
00181                 empty = false;
00182                 break;
00183             }
00184         }
00185     }
00186     else
00187     {
00188         EXCEPTION("The path '" << mAbsPath << "' does not exist.");
00189     }
00190     return empty;
00191 }
00192 
00193 std::string FileFinder::GetAbsolutePath() const
00194 {
00195     if (IsDir())
00196     {
00197         return mAbsPath + '/';
00198     }
00199     return mAbsPath;
00200 }
00201 
00202 bool FileFinder::IsNewerThan(const FileFinder& rOtherEntity) const
00203 {
00204     assert(Exists());
00205     assert(rOtherEntity.Exists());
00206     return fs::last_write_time(mAbsPath) > fs::last_write_time(rOtherEntity.mAbsPath);
00207 }
00208 
00209 std::string FileFinder::GetLeafName() const
00210 {
00211     return fs::path(mAbsPath).leaf();
00212 }
00213 
00214 std::string FileFinder::GetLeafNameNoExtension() const
00215 {
00216     return fs::basename(mAbsPath);
00217 }
00218 
00219 std::string FileFinder::GetExtension() const
00220 {
00221     return fs::extension(mAbsPath);
00222 }
00223 
00224 FileFinder FileFinder::GetParent() const
00225 {
00226     fs::path our_path(mAbsPath);
00227     EXCEPT_IF_NOT(our_path.has_branch_path());
00228     return FileFinder(our_path.branch_path().string(),
00229                       RelativeTo::Absolute);
00230 }
00231 
00232 
00233 std::string FileFinder::GetRelativePath(const FileFinder& rBasePath) const
00234 {
00235     const std::string base_path = rBasePath.GetAbsolutePath();
00236     const std::string our_path = GetAbsolutePath();
00237     if (our_path.substr(0, base_path.length()) != base_path)
00238     {
00239         EXCEPTION("The path '" << our_path << "' is not relative to '" << base_path << "'.");
00240     }
00241     return our_path.substr(base_path.length());
00242 }
00243 
00244 
00245 FileFinder FileFinder::CopyTo(const FileFinder& rDest) const
00246 {
00247     if (!Exists())
00248     {
00249         EXCEPTION("Cannot copy '" << mAbsPath << "' as it does not exist.");
00250     }
00251     if (!IsFile())
00252     {
00253         EXCEPTION("Only single files may be copied; " << mAbsPath << " is not a file.");
00254     }
00255     fs::path from_path(mAbsPath);
00256     fs::path to_path(rDest.mAbsPath);
00257     if (rDest.IsDir())
00258     {
00259         to_path /= from_path.leaf();
00260     }
00261     if (fs::exists(to_path))
00262     {
00263         fs::remove(to_path);
00264     }
00265     fs::copy_file(from_path, to_path);
00266     return FileFinder(to_path);
00267 }
00268 
00269 
00274 void RemoveAll(const fs::path& rPath)
00275 {
00276     // First recursively remove any children
00277     if (fs::is_directory(rPath))
00278     {
00279         fs::directory_iterator end_iter;
00280         for (fs::directory_iterator dir_iter(rPath); dir_iter != end_iter; ++dir_iter)
00281         {
00282             RemoveAll(dir_iter->path());
00283         }
00284     }
00285     // Now remove the item itself
00286     fs::remove(rPath);
00287 }
00288 
00289 void FileFinder::Remove(bool force) const
00290 {
00291     // Test for bad paths
00292     const std::string test_output(OutputFileHandler::GetChasteTestOutputDirectory());
00293     if (GetAbsolutePath().substr(0, test_output.length()) != test_output)
00294     {
00295         EXCEPTION("Cannot remove location '" << mAbsPath
00296                   << "' as it is not located within the Chaste test output folder ("
00297                   << test_output << ").");
00298     }
00299     if (mAbsPath.find("..") != std::string::npos)
00300     {
00301         EXCEPTION("Cannot remove location '" << mAbsPath
00302                   << "' as it contains a dangerous path component.");
00303     }
00304     if (Exists())
00305     {
00306         if (!force)
00307         {
00308             fs::path sig_file(mAbsPath);
00309             if (IsFile())
00310             {
00311                 // We need to look for the signature file in the parent folder
00312                 sig_file.remove_leaf();
00313             }
00314             sig_file /= OutputFileHandler::SIG_FILE_NAME;
00315             if (!fs::exists(sig_file))
00316             {
00317                 EXCEPTION("Cannot remove location '" << mAbsPath << "' because the signature file '"
00318                           << OutputFileHandler::SIG_FILE_NAME << "' is not present.");
00319             }
00320         }
00321         // Do the removal
00322         RemoveAll(mAbsPath);
00323     }
00324 }
00325 
00326 
00327 std::vector<FileFinder> FileFinder::FindMatches(const std::string& rPattern) const
00328 {
00329     // Check for error/warning cases
00330     if (!IsDir())
00331     {
00332         EXCEPTION("Cannot search for matching files in '" << mAbsPath << "' as it is not a directory.");
00333     }
00334     size_t len = rPattern.length();
00335     size_t inner_star_pos = rPattern.find('*', 1);
00336     if (inner_star_pos != std::string::npos && inner_star_pos < len - 1)
00337     {
00338         WARNING("A '*' only has special meaning at the start or end of a pattern.");
00339     }
00340 
00341     // Note initial or trailing *, and use of ?
00342     std::string pattern(rPattern);
00343     bool star_fini = false;
00344     if (!pattern.empty() && *(pattern.rbegin()) == '*')
00345     {
00346         star_fini = true;
00347         pattern = pattern.substr(0, len-1);
00348         len--;
00349     }
00350     bool star_init = false;
00351     if (!pattern.empty() && pattern[0] == '*')
00352     {
00353         star_init = true;
00354         pattern = pattern.substr(1);
00355         len--;
00356     }
00357     bool has_query = (pattern.find('?') != std::string::npos);
00358     // Disallow a harder case to match
00359     if (star_init && star_fini && has_query)
00360     {
00361         EXCEPTION("The '*' wildcard may not be used at both the start and end of the pattern if the '?' wildcard is also used.");
00362     }
00363 
00364     // Search the folder
00365     std::vector<FileFinder> results;
00366     if (!rPattern.empty())
00367     {
00368         fs::directory_iterator end_iter;
00369         fs::path our_path(mAbsPath);
00370         for (fs::directory_iterator dir_iter(our_path); dir_iter != end_iter; ++dir_iter)
00371         {
00372             std::string leafname = dir_iter->path().leaf();
00373             size_t leaf_len = leafname.length();
00374             if (leafname[0] != '.'  // Don't include hidden files
00375                 && leaf_len >= len) // Ignore stuff that can't match
00376             {
00377                 if (!has_query) // Easier case
00378                 {
00379                     size_t pos = leafname.find(pattern);
00380                     if ((star_init || pos == 0) && (star_fini || pos + len == leaf_len))
00381                     {
00382                         results.push_back(FileFinder(our_path / leafname));
00383                     }
00384                 }
00385                 else
00386                 {
00387                     std::string match;
00388                     if (star_init)
00389                     {
00390                         // Match against last len chars
00391                         match = leafname.substr(leaf_len - len);
00392                     }
00393                     else
00394                     {
00395                         // Match against first len chars
00396                         match = leafname.substr(0, len);
00397                     }
00398                     bool ok = true;
00399                     for (std::string::const_iterator it_p=pattern.begin(), it_m=match.begin();
00400                          it_p != pattern.end();
00401                          ++it_p, ++it_m)
00402                     {
00403                         if (*it_p != '?' && *it_p != *it_m)
00404                         {
00405                             ok = false;
00406                             break;
00407                         }
00408                     }
00409                     if (ok)
00410                     {
00411                         results.push_back(FileFinder(our_path / leafname));
00412                     }
00413                 }
00414             }
00415         }
00416     }
00417     return results;
00418 }
00419 
00420 
00421 bool FileFinder::IsAbsolutePath(const std::string& rPath)
00422 {
00423     return fs::path(rPath).is_complete();
00424 }
00425 
00426 void FileFinder::ReplaceSpacesWithUnderscores(std::string& rPath)
00427 {
00428     for (std::string::iterator it = rPath.begin(); it != rPath.end(); ++it)
00429     {
00430         if (*it == ' ')
00431         {
00432             *it = '_';
00433         }
00434     }
00435 }
00436 
00437 void FileFinder::ReplaceUnderscoresWithSpaces(std::string& rPath)
00438 {
00439     for (std::string::iterator it = rPath.begin(); it != rPath.end(); ++it)
00440     {
00441         if (*it == '_')
00442         {
00443             *it = ' ';
00444         }
00445     }
00446 }
00447 
00448 void FileFinder::FakePath(RelativeTo::Value fakeWhat, const std::string& rFakePath)
00449 {
00450     msFakeWhat = fakeWhat;
00451     msFakePath = rFakePath;
00452     msFaking = true;
00453 }
00454 
00455 void FileFinder::StopFaking()
00456 {
00457     msFaking = false;
00458 }