Chaste Release::3.1
|
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 }