Cppcheck
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
path.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2016 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 #include "path.h"
23 #include "utils.h"
24 #include <algorithm>
25 #include <vector>
26 #include <sstream>
27 #include <cstring>
28 #include <cctype>
29 #ifndef _WIN32
30 #include <unistd.h>
31 #else
32 #include <direct.h>
33 #endif
34 
35 /** Is the filesystem case insensitive? */
37 {
38 #ifdef _WIN32
39  return true;
40 #else
41  // TODO: Non-windows filesystems might be case insensitive
42  return false;
43 #endif
44 }
45 
46 std::string Path::toNativeSeparators(std::string path)
47 {
48 #if defined(_WIN32)
49  const char separ = '/';
50  const char native = '\\';
51 #else
52  const char separ = '\\';
53  const char native = '/';
54 #endif
55  std::replace(path.begin(), path.end(), separ, native);
56  return path;
57 }
58 
59 std::string Path::fromNativeSeparators(std::string path)
60 {
61  const char nonnative = '\\';
62  const char newsepar = '/';
63  std::replace(path.begin(), path.end(), nonnative, newsepar);
64  return path;
65 }
66 
67 std::string Path::simplifyPath(std::string originalPath)
68 {
69  const bool isUnc = originalPath.compare(0,2,"//") == 0;
70 
71  // Remove ./, .//, ./// etc. at the beginning
72  while (originalPath.compare(0,2,"./") == 0) { // remove "./././"
73  const size_t toErase = originalPath.find_first_not_of('/', 2);
74  originalPath = originalPath.erase(0, toErase);
75  }
76 
77  std::string subPath;
78  std::vector<std::string> pathParts;
79  for (std::size_t i = 0; i < originalPath.size(); ++i) {
80  if (originalPath[i] == '/' || originalPath[i] == '\\') {
81  if (subPath.length() > 0) {
82  pathParts.push_back(subPath);
83  subPath = "";
84  }
85 
86  pathParts.push_back(std::string(1 , originalPath[i]));
87  } else
88  subPath.append(1, originalPath[i]);
89  }
90 
91  if (subPath.length() > 0)
92  pathParts.push_back(subPath);
93 
94  // First filter out all double slashes
95  for (unsigned int i = 1; i < pathParts.size(); ++i) {
96  if (i > 0 && pathParts[i] == "/" && pathParts[i-1] == "/") {
97  pathParts.erase(pathParts.begin() + static_cast<int>(i) - 1);
98  --i;
99  }
100  }
101 
102  for (unsigned int i = 1; i < pathParts.size(); ++i) {
103  if (i > 1 && pathParts[i-2] != ".." && pathParts[i] == ".." && pathParts.size() > i + 1) {
104  pathParts.erase(pathParts.begin() + static_cast<int>(i) + 1);
105  pathParts.erase(pathParts.begin() + static_cast<int>(i));
106  pathParts.erase(pathParts.begin() + static_cast<int>(i) - 1);
107  pathParts.erase(pathParts.begin() + static_cast<int>(i) - 2);
108  i = 0;
109  } else if (i > 0 && pathParts[i] == ".") {
110  pathParts.erase(pathParts.begin() + static_cast<int>(i));
111  i = 0;
112  } else if (i > 0 && pathParts[i] == "/" && pathParts[i-1] == "/") {
113  pathParts.erase(pathParts.begin() + static_cast<int>(i) - 1);
114  i = 0;
115  }
116  }
117 
118  if (isUnc) {
119  // Restore the leading double slash
120  pathParts.insert(pathParts.begin(), "/");
121  }
122 
123  std::ostringstream oss;
124  for (std::vector<std::string>::size_type i = 0; i < pathParts.size(); ++i) {
125  oss << pathParts[i];
126  }
127 
128  return oss.str();
129 }
130 
131 std::string Path::getPathFromFilename(const std::string &filename)
132 {
133  const std::size_t pos = filename.find_last_of("\\/");
134 
135  if (pos != std::string::npos)
136  return filename.substr(0, 1 + pos);
137 
138  return "";
139 }
140 
141 
142 bool Path::sameFileName(const std::string &fname1, const std::string &fname2)
143 {
144 #if defined(__linux__) || defined(__sun) || defined(__hpux)
145  return bool(fname1 == fname2);
146 #elif defined(_MSC_VER) || (defined(__GNUC__) && defined(_WIN32))
147  return bool(_stricmp(fname1.c_str(), fname2.c_str()) == 0);
148 #elif defined(__GNUC__)
149  return bool(strcasecmp(fname1.c_str(), fname2.c_str()) == 0);
150 #elif defined(__BORLANDC__)
151  return bool(stricmp(fname1.c_str(), fname2.c_str()) == 0);
152 #else
153 #error Platform filename compare function needed
154 #endif
155 }
156 
157 // This wrapper exists because Sun's CC does not allow a static_cast
158 // from extern "C" int(*)(int) to int(*)(int).
159 static int tolowerWrapper(int c)
160 {
161  return std::tolower(c);
162 }
163 
164 std::string Path::removeQuotationMarks(std::string path)
165 {
166  path.erase(std::remove(path.begin(), path.end(), '\"'), path.end());
167  return path;
168 }
169 
170 std::string Path::getFilenameExtension(const std::string &path)
171 {
172  const std::string::size_type dotLocation = path.find_last_of('.');
173  if (dotLocation == std::string::npos)
174  return "";
175 
176  std::string extension = path.substr(dotLocation);
178  // on a case insensitive filesystem the case doesn't matter so
179  // let's return the extension in lowercase
180  std::transform(extension.begin(), extension.end(), extension.begin(), tolowerWrapper);
181  }
182  return extension;
183 }
184 
185 std::string Path::getFilenameExtensionInLowerCase(const std::string &path)
186 {
187  std::string extension = getFilenameExtension(path);
188  std::transform(extension.begin(), extension.end(), extension.begin(), tolowerWrapper);
189  return extension;
190 }
191 
192 const std::string Path::getCurrentPath()
193 {
194  char currentPath[4096];
195 
196 #ifndef _WIN32
197  if (getcwd(currentPath, 4096) != 0)
198 #else
199  if (_getcwd(currentPath, 4096) != 0)
200 #endif
201  return std::string(currentPath);
202 
203  return emptyString;
204 }
205 
206 bool Path::isAbsolute(const std::string& path)
207 {
208  const std::string& nativePath = toNativeSeparators(path);
209 
210 #ifdef _WIN32
211  if (path.length() < 2)
212  return false;
213 
214  // On Windows, 'C:\foo\bar' is an absolute path, while 'C:foo\bar' is not
215  if (nativePath.compare(0, 2, "\\\\") == 0 || (std::isalpha(nativePath[0]) != 0 && nativePath.compare(1, 2, ":\\") == 0))
216  return true;
217 #else
218  if (!nativePath.empty() && nativePath[0] == '/')
219  return true;
220 #endif
221 
222  return false;
223 }
224 
225 std::string Path::getRelativePath(const std::string& absolutePath, const std::vector<std::string>& basePaths)
226 {
227  for (std::vector<std::string>::const_iterator i = basePaths.begin(); i != basePaths.end(); ++i) {
228  if (absolutePath == *i || i->empty()) // Seems to be a file, or path is empty
229  continue;
230 
231  bool endsWithSep = endsWith(*i,'/');
232  if (absolutePath.compare(0, i->length(), *i) == 0 && absolutePath[i->length() - (endsWithSep?1:0)] == '/') {
233  std::string rest = absolutePath.substr(i->length() + (endsWithSep?0:1));
234  return rest;
235  }
236  }
237  return absolutePath;
238 }
239 
240 bool Path::isC(const std::string &path)
241 {
242  // In unix, ".C" is considered C++ file
243  const std::string extension = getFilenameExtension(path);
244  return (extension == ".c");
245 }
246 
247 bool Path::isCPP(const std::string &path)
248 {
249  const std::string extension = getFilenameExtensionInLowerCase(path);
250  if (extension == ".cpp" ||
251  extension == ".cxx" ||
252  extension == ".cc" ||
253  extension == ".c++" ||
254  extension == ".hpp" ||
255  extension == ".hxx" ||
256  extension == ".hh" ||
257  extension == ".tpp" ||
258  extension == ".txx") {
259  return true;
260  }
261 
262  // In unix, ".C" is considered C++ file
263  return (getFilenameExtension(path) == ".C");
264 }
265 
266 bool Path::acceptFile(const std::string &path, const std::set<std::string> &extra)
267 {
268  return !Path::isHeader(path) && (Path::isCPP(path) || Path::isC(path) || extra.find(getFilenameExtension(path)) != extra.end());
269 }
270 
271 bool Path::isHeader(const std::string &path)
272 {
273  const std::string extension = getFilenameExtensionInLowerCase(path);
274  return (extension.compare(0, 2, ".h") == 0);
275 }
276 
277 std::string Path::getAbsoluteFilePath(const std::string& filePath)
278 {
279  std::string absolute_path;
280 #ifdef _WIN32
281  char absolute[_MAX_PATH];
282  if (_fullpath(absolute, filePath.c_str(), _MAX_PATH))
283  absolute_path = absolute;
284 #elif defined(__linux__) || defined(__sun) || defined(__hpux) || defined(__GNUC__)
285  char * absolute = realpath(filePath.c_str(), nullptr);
286  if (absolute)
287  absolute_path = absolute;
288  free(absolute);
289 #else
290 #error Platform absolute path function needed
291 #endif
292  return absolute_path;
293 }