Cppcheck
path.cpp
Go to the documentation of this file.
00001 /*
00002  * Cppcheck - A tool for static C/C++ code analysis
00003  * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team.
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation, either version 3 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include <algorithm>
00020 #include <vector>
00021 #include <sstream>
00022 #include <cstring>
00023 #include <cctype>
00024 #include "path.h"
00025 
00026 /** Is the filesystem case insensitive? */
00027 static bool caseInsensitiveFilesystem()
00028 {
00029 #ifdef _WIN32
00030     return true;
00031 #else
00032     // TODO: Non-windows filesystems might be case insensitive
00033     return false;
00034 #endif
00035 }
00036 
00037 std::string Path::toNativeSeparators(std::string path)
00038 {
00039 #if defined(_WIN32)
00040     char separ = '/';
00041     char native = '\\';
00042 #else
00043     char separ = '\\';
00044     char native = '/';
00045 #endif
00046     std::replace(path.begin(), path.end(), separ, native);
00047     return path;
00048 }
00049 
00050 std::string Path::fromNativeSeparators(std::string path)
00051 {
00052     char nonnative = '\\';
00053     char newsepar = '/';
00054     std::replace(path.begin(), path.end(), nonnative, newsepar);
00055     return path;
00056 }
00057 
00058 std::string Path::simplifyPath(const char *originalPath)
00059 {
00060     // Skip ./ at the beginning
00061     if (std::strlen(originalPath) > 2 && originalPath[0] == '.' &&
00062         originalPath[1] == '/') {
00063         originalPath += 2;
00064     }
00065 
00066     std::string subPath = "";
00067     std::vector<std::string> pathParts;
00068     for (; *originalPath; ++originalPath) {
00069         if (*originalPath == '/' || *originalPath == '\\') {
00070             if (subPath.length() > 0) {
00071                 pathParts.push_back(subPath);
00072                 subPath = "";
00073             }
00074 
00075             pathParts.push_back(std::string(1 , *originalPath));
00076         } else
00077             subPath.append(1, *originalPath);
00078     }
00079 
00080     if (subPath.length() > 0)
00081         pathParts.push_back(subPath);
00082 
00083     for (unsigned int i = 1; i < pathParts.size(); ++i) {
00084         if (i > 1 && pathParts[i-2] != ".." && pathParts[i] == ".." && pathParts.size() > i + 1) {
00085             pathParts.erase(pathParts.begin() + static_cast<int>(i) + 1);
00086             pathParts.erase(pathParts.begin() + static_cast<int>(i));
00087             pathParts.erase(pathParts.begin() + static_cast<int>(i) - 1);
00088             pathParts.erase(pathParts.begin() + static_cast<int>(i) - 2);
00089             i = 0;
00090         } else if (i > 0 && pathParts[i] == ".") {
00091             pathParts.erase(pathParts.begin() + static_cast<int>(i));
00092             i = 0;
00093         } else if (i > 0 && pathParts[i] == "/" && pathParts[i-1] == "/") {
00094             pathParts.erase(pathParts.begin() + static_cast<int>(i) - 1);
00095             i = 0;
00096         }
00097     }
00098 
00099     std::ostringstream oss;
00100     for (std::vector<std::string>::size_type i = 0; i < pathParts.size(); ++i) {
00101         oss << pathParts[i];
00102     }
00103 
00104     return oss.str();
00105 }
00106 
00107 std::string Path::getPathFromFilename(const std::string &filename)
00108 {
00109     std::string path = "";
00110 
00111     std::size_t pos = filename.find_last_of("\\/");
00112 
00113     if (pos != std::string::npos)
00114         path = filename.substr(0, 1 + pos);
00115 
00116     return path;
00117 }
00118 
00119 
00120 bool Path::sameFileName(const std::string &fname1, const std::string &fname2)
00121 {
00122 #if defined(__linux__) || defined(__sun) || defined(__hpux)
00123     return bool(fname1 == fname2);
00124 #elif defined(__GNUC__)
00125     return bool(strcasecmp(fname1.c_str(), fname2.c_str()) == 0);
00126 #elif defined(__BORLANDC__)
00127     return bool(stricmp(fname1.c_str(), fname2.c_str()) == 0);
00128 #elif defined(_MSC_VER)
00129     return bool(_stricmp(fname1.c_str(), fname2.c_str()) == 0);
00130 #else
00131 #error Platform filename compare function needed
00132 #endif
00133 }
00134 
00135 // This wrapper exists because Sun's CC does not allow a static_cast
00136 // from extern "C" int(*)(int) to int(*)(int).
00137 static int tolowerWrapper(int c)
00138 {
00139     return std::tolower(c);
00140 }
00141 
00142 std::string Path::removeQuotationMarks(std::string path)
00143 {
00144     path.erase(std::remove(path.begin(), path.end(), '\"'), path.end());
00145     return path;
00146 }
00147 
00148 std::string Path::getFilenameExtension(const std::string &path)
00149 {
00150     const std::string::size_type dotLocation = path.find_last_of('.');
00151     if (dotLocation == std::string::npos)
00152         return "";
00153 
00154     std::string extension = path.substr(dotLocation);
00155     if (caseInsensitiveFilesystem()) {
00156         // on a case insensitive filesystem the case doesn't matter so
00157         // let's return the extension in lowercase
00158         std::transform(extension.begin(), extension.end(), extension.begin(), tolowerWrapper);
00159     }
00160     return extension;
00161 }
00162 
00163 std::string Path::getFilenameExtensionInLowerCase(const std::string &path)
00164 {
00165     std::string extension = getFilenameExtension(path);
00166     std::transform(extension.begin(), extension.end(), extension.begin(), tolowerWrapper);
00167     return extension;
00168 }
00169 
00170 std::string Path::getRelativePath(const std::string& absolutePath, const std::vector<std::string>& basePaths)
00171 {
00172     for (std::vector<std::string>::const_iterator i = basePaths.begin(); i != basePaths.end(); ++i) {
00173         if (absolutePath == *i || i->empty()) // Seems to be a file, or path is empty
00174             continue;
00175 
00176         bool endsWithSep = (*i)[i->length()-1] == '/';
00177         if (absolutePath.compare(0, i->length(), *i) == 0 && absolutePath[i->length() - (endsWithSep?1:0)] == '/') {
00178             std::string rest = absolutePath.substr(i->length() + (endsWithSep?0:1));
00179             return rest;
00180         }
00181     }
00182     return absolutePath;
00183 }
00184 
00185 bool Path::isC(const std::string &path)
00186 {
00187     // In unix, ".C" is considered C++ file
00188     const std::string extension = getFilenameExtension(path);
00189     return(extension == ".c");
00190 }
00191 
00192 bool Path::isCPP(const std::string &path)
00193 {
00194     const std::string extension = getFilenameExtensionInLowerCase(path);
00195     if (extension == ".cpp" ||
00196         extension == ".cxx" ||
00197         extension == ".cc" ||
00198         extension == ".c++" ||
00199         extension == ".hpp" ||
00200         extension == ".tpp" ||
00201         extension == ".txx") {
00202         return true;
00203     }
00204 
00205     // In unix, ".C" is considered C++ file
00206     return(getFilenameExtension(path) == ".C");
00207 }
00208 
00209 bool Path::acceptFile(const std::string &path)
00210 {
00211     return !Path::isHeader(path) && (Path::isCPP(path) || Path::isC(path));
00212 }
00213 
00214 bool Path::isHeader(const std::string &path)
00215 {
00216     const std::string extension = getFilenameExtensionInLowerCase(path);
00217     return (extension.compare(0, 2, ".h") == 0);
00218 }