LCOV - code coverage report
Current view: top level - lib - path.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 132 133 99.2 %
Date: 2024-04-28 12:00:40 Functions: 25 25 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Cppcheck - A tool for static C/C++ code analysis
       3             :  * Copyright (C) 2007-2024 Cppcheck team.
       4             :  *
       5             :  * This program is free software: you can redistribute it and/or modify
       6             :  * it under the terms of the GNU General Public License as published by
       7             :  * the Free Software Foundation, either version 3 of the License, or
       8             :  * (at your option) any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :  * GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License
      16             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      17             :  */
      18             : 
      19             : #if defined(__GNUC__) && (defined(_WIN32) || defined(__CYGWIN__))
      20             : #undef __STRICT_ANSI__
      21             : #endif
      22             : 
      23             : #include "path.h"
      24             : #include "utils.h"
      25             : 
      26             : #include <algorithm>
      27             : #include <cstdlib>
      28             : #include <sys/stat.h>
      29             : #include <unordered_set>
      30             : #include <utility>
      31             : 
      32             : #include <simplecpp.h>
      33             : 
      34             : #ifndef _WIN32
      35             : #include <sys/types.h>
      36             : #include <unistd.h>
      37             : #else
      38             : #include <direct.h>
      39             : #include <windows.h>
      40             : #endif
      41             : #if defined(__CYGWIN__)
      42             : #include <strings.h>
      43             : #endif
      44             : #if defined(__APPLE__)
      45             : #include <mach-o/dyld.h>
      46             : #endif
      47             : 
      48             : 
      49             : /** Is the filesystem case insensitive? */
      50       59423 : static constexpr bool caseInsensitiveFilesystem()
      51             : {
      52             : #if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))
      53             :     // Windows is case insensitive
      54             :     // MacOS is case insensitive by default (also supports case sensitivity)
      55             :     return true;
      56             : #else
      57             :     // TODO: Non-windows filesystems might be case insensitive
      58       59423 :     return false;
      59             : #endif
      60             : }
      61             : 
      62       21717 : std::string Path::toNativeSeparators(std::string path)
      63             : {
      64             : #if defined(_WIN32)
      65             :     constexpr char separ = '/';
      66             :     constexpr char native = '\\';
      67             : #else
      68       21717 :     constexpr char separ = '\\';
      69       21717 :     constexpr char native = '/';
      70             : #endif
      71       21717 :     std::replace(path.begin(), path.end(), separ, native);
      72       21715 :     return path;
      73             : }
      74             : 
      75        1646 : std::string Path::fromNativeSeparators(std::string path)
      76             : {
      77        1646 :     constexpr char nonnative = '\\';
      78        1646 :     constexpr char newsepar = '/';
      79        1646 :     std::replace(path.begin(), path.end(), nonnative, newsepar);
      80        1646 :     return path;
      81             : }
      82             : 
      83       28293 : std::string Path::simplifyPath(std::string originalPath)
      84             : {
      85       28293 :     return simplecpp::simplifyPath(std::move(originalPath));
      86             : }
      87             : 
      88         839 : std::string Path::getPathFromFilename(const std::string &filename)
      89             : {
      90         839 :     const std::size_t pos = filename.find_last_of("\\/");
      91             : 
      92         839 :     if (pos != std::string::npos)
      93         812 :         return filename.substr(0, 1 + pos);
      94             : 
      95          27 :     return "";
      96             : }
      97             : 
      98           7 : bool Path::sameFileName(const std::string &fname1, const std::string &fname2)
      99             : {
     100           7 :     return caseInsensitiveFilesystem() ? (caseInsensitiveStringCompare(fname1, fname2) == 0) : (fname1 == fname2);
     101             : }
     102             : 
     103         205 : std::string Path::removeQuotationMarks(std::string path)
     104             : {
     105         205 :     path.erase(std::remove(path.begin(), path.end(), '\"'), path.end());
     106         205 :     return path;
     107             : }
     108             : 
     109      163945 : std::string Path::getFilenameExtension(const std::string &path, bool lowercase)
     110             : {
     111      163945 :     const std::string::size_type dotLocation = path.find_last_of('.');
     112      163946 :     if (dotLocation == std::string::npos)
     113         177 :         return "";
     114             : 
     115      327529 :     std::string extension = path.substr(dotLocation);
     116      163768 :     if (lowercase || caseInsensitiveFilesystem()) {
     117             :         // on a case insensitive filesystem the case doesn't matter so
     118             :         // let's return the extension in lowercase
     119      129363 :         strTolower(extension);
     120             :     }
     121      163761 :     return extension;
     122             : }
     123             : 
     124      129486 : std::string Path::getFilenameExtensionInLowerCase(const std::string &path)
     125             : {
     126      129486 :     return getFilenameExtension(path, true);
     127             : }
     128             : 
     129         100 : std::string Path::getCurrentPath()
     130             : {
     131             :     char currentPath[4096];
     132             : 
     133             : #ifndef _WIN32
     134         100 :     if (getcwd(currentPath, 4096) != nullptr)
     135             : #else
     136             :     if (_getcwd(currentPath, 4096) != nullptr)
     137             : #endif
     138         100 :         return std::string(currentPath);
     139             : 
     140           0 :     return "";
     141             : }
     142             : 
     143         884 : std::string Path::getCurrentExecutablePath(const char* fallback)
     144             : {
     145         884 :     char buf[4096] = {};
     146         884 :     bool success{};
     147             : #ifdef _WIN32
     148             :     success = (GetModuleFileNameA(nullptr, buf, sizeof(buf)) < sizeof(buf));
     149             : #elif defined(__APPLE__)
     150             :     uint32_t size = sizeof(buf);
     151             :     success = (_NSGetExecutablePath(buf, &size) == 0);
     152             : #else
     153         884 :     const char* procPath =
     154             : #ifdef __SVR4 // Solaris
     155             :         "/proc/self/path/a.out";
     156             : #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
     157             :         "/proc/curproc/file";
     158             : #else // Linux
     159             :         "/proc/self/exe";
     160             : #endif
     161         884 :     success = (readlink(procPath, buf, sizeof(buf)) != -1);
     162             : #endif
     163        1768 :     return success ? std::string(buf) : std::string(fallback);
     164             : }
     165             : 
     166          81 : bool Path::isAbsolute(const std::string& path)
     167             : {
     168          81 :     const std::string& nativePath = toNativeSeparators(path);
     169             : 
     170             : #ifdef _WIN32
     171             :     if (path.length() < 2)
     172             :         return false;
     173             : 
     174             :     // On Windows, 'C:\foo\bar' is an absolute path, while 'C:foo\bar' is not
     175             :     return startsWith(nativePath, "\\\\") || (std::isalpha(nativePath[0]) != 0 && nativePath.compare(1, 2, ":\\") == 0);
     176             : #else
     177         162 :     return !nativePath.empty() && nativePath[0] == '/';
     178             : #endif
     179             : }
     180             : 
     181          31 : std::string Path::getRelativePath(const std::string& absolutePath, const std::vector<std::string>& basePaths)
     182             : {
     183          44 :     for (const std::string &bp : basePaths) {
     184          19 :         if (absolutePath == bp || bp.empty()) // Seems to be a file, or path is empty
     185           6 :             continue;
     186             : 
     187          13 :         if (absolutePath.compare(0, bp.length(), bp) != 0)
     188           6 :             continue;
     189             : 
     190           7 :         if (endsWith(bp,'/'))
     191           8 :             return absolutePath.substr(bp.length());
     192           5 :         if (absolutePath.size() > bp.size() && absolutePath[bp.length()] == '/')
     193           4 :             return absolutePath.substr(bp.length() + 1);
     194             :     }
     195          25 :     return absolutePath;
     196             : }
     197             : 
     198             : static const std::unordered_set<std::string> cpp_src_exts = {
     199             :     ".cpp", ".cxx", ".cc", ".c++", ".tpp", ".txx", ".ipp", ".ixx"
     200             : };
     201             : 
     202             : static const std::unordered_set<std::string> c_src_exts = {
     203             :     ".c", ".cl"
     204             : };
     205             : 
     206             : static const std::unordered_set<std::string> header_exts = {
     207             :     ".h", ".hpp", ".h++", ".hxx", ".hh"
     208             : };
     209             : 
     210           9 : bool Path::isC(const std::string &path)
     211             : {
     212             :     // In unix, ".C" is considered C++ file
     213           9 :     const std::string extension = getFilenameExtension(path);
     214          15 :     return extension == ".c" ||
     215          24 :            extension == ".cl";
     216             : }
     217             : 
     218          16 : bool Path::isCPP(const std::string &path)
     219             : {
     220          16 :     const std::string extension = getFilenameExtensionInLowerCase(path);
     221          11 :     return extension == ".cpp" ||
     222          21 :            extension == ".cxx" ||
     223          19 :            extension == ".cc" ||
     224          17 :            extension == ".c++" ||
     225          15 :            extension == ".hpp" ||
     226          14 :            extension == ".hxx" ||
     227          14 :            extension == ".hh" ||
     228          13 :            extension == ".tpp" ||
     229          11 :            extension == ".txx" ||
     230           9 :            extension == ".ipp" ||
     231          31 :            extension == ".ixx" ||
     232          35 :            getFilenameExtension(path) == ".C"; // In unix, ".C" is considered C++ file
     233             : }
     234             : 
     235        2719 : bool Path::acceptFile(const std::string &path, const std::set<std::string> &extra)
     236             : {
     237        2719 :     bool header = false;
     238        2719 :     return (identify(path, &header) != Standards::Language::None && !header) || extra.find(getFilenameExtension(path)) != extra.end();
     239             : }
     240             : 
     241             : // cppcheck-suppress unusedFunction
     242          10 : bool Path::isHeader(const std::string &path)
     243             : {
     244          20 :     const std::string extension = getFilenameExtensionInLowerCase(path);
     245          20 :     return startsWith(extension, ".h");
     246             : }
     247             : 
     248       31449 : Standards::Language Path::identify(const std::string &path, bool *header)
     249             : {
     250             :     // cppcheck-suppress uninitvar - TODO: FP
     251       31449 :     if (header)
     252        2736 :         *header = false;
     253             : 
     254       62898 :     std::string ext = getFilenameExtension(path);
     255       31449 :     if (ext == ".C")
     256           2 :         return Standards::Language::CPP;
     257       31447 :     if (c_src_exts.find(ext) != c_src_exts.end())
     258        6432 :         return Standards::Language::C;
     259             :     // cppcheck-suppress knownConditionTrueFalse - TODO: FP
     260       25012 :     if (!caseInsensitiveFilesystem())
     261       25012 :         strTolower(ext);
     262       25015 :     if (ext == ".h") {
     263         162 :         if (header)
     264         157 :             *header = true;
     265         162 :         return Standards::Language::C; // treat as C for now
     266             :     }
     267       24853 :     if (cpp_src_exts.find(ext) != cpp_src_exts.end())
     268       22593 :         return Standards::Language::CPP;
     269        2260 :     if (header_exts.find(ext) != header_exts.end()) {
     270          12 :         if (header)
     271          11 :             *header = true;
     272          12 :         return Standards::Language::CPP;
     273             :     }
     274        2248 :     return Standards::Language::None;
     275             : }
     276             : 
     277          10 : bool Path::isHeader2(const std::string &path)
     278             : {
     279             :     bool header;
     280          10 :     (void)Path::identify(path, &header);
     281          10 :     return header;
     282             : }
     283             : 
     284         600 : std::string Path::getAbsoluteFilePath(const std::string& filePath)
     285             : {
     286         600 :     std::string absolute_path;
     287             : #ifdef _WIN32
     288             :     char absolute[_MAX_PATH];
     289             :     if (_fullpath(absolute, filePath.c_str(), _MAX_PATH))
     290             :         absolute_path = absolute;
     291             : #elif defined(__linux__) || defined(__sun) || defined(__hpux) || defined(__GNUC__) || defined(__CPPCHECK__)
     292         600 :     char * absolute = realpath(filePath.c_str(), nullptr);
     293         600 :     if (absolute)
     294         600 :         absolute_path = absolute;
     295         600 :     free(absolute);
     296             : #else
     297             : #error Platform absolute path function needed
     298             : #endif
     299         600 :     return absolute_path;
     300             : }
     301             : 
     302     1210823 : std::string Path::stripDirectoryPart(const std::string &file)
     303             : {
     304             : #if defined(_WIN32) && !defined(__MINGW32__)
     305             :     constexpr char native = '\\';
     306             : #else
     307     1210823 :     constexpr char native = '/';
     308             : #endif
     309             : 
     310     1210823 :     const std::string::size_type p = file.rfind(native);
     311     1210823 :     if (p != std::string::npos) {
     312     1210465 :         return file.substr(p + 1);
     313             :     }
     314         358 :     return file;
     315             : }
     316             : 
     317             : #ifdef _WIN32
     318             : using mode_t = unsigned short;
     319             : #endif
     320             : 
     321        2674 : static mode_t file_type(const std::string &path)
     322             : {
     323             :     struct stat file_stat;
     324        2674 :     if (stat(path.c_str(), &file_stat) == -1)
     325        2638 :         return 0;
     326          36 :     return file_stat.st_mode & S_IFMT;
     327             : }
     328             : 
     329        2640 : bool Path::isFile(const std::string &path)
     330             : {
     331        2640 :     return file_type(path) == S_IFREG;
     332             : }
     333             : 
     334          34 : bool Path::isDirectory(const std::string &path)
     335             : {
     336          34 :     return file_type(path) == S_IFDIR;
     337             : }
     338             : 
     339        2432 : std::string Path::join(const std::string& path1, const std::string& path2) {
     340        2432 :     if (path1.empty() || path2.empty())
     341        2399 :         return path1 + path2;
     342          33 :     if (path2.front() == '/')
     343           1 :         return path2;
     344          64 :     return ((path1.back() == '/') ? path1 : (path1 + "/")) + path2;
     345             : }

Generated by: LCOV version 1.14