1
2
3
4
5
6 #include <soulng/util/Path.hpp>
7 #include <soulng/util/TextUtils.hpp>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <stdlib.h>
11 #include <vector>
12 #include <string>
13 #include <cctype>
14 #ifdef _WIN32
15 #include <direct.h>
16 #elif defined(__linux) || defined(__unix) || defined(__posix) || defined(__unix__)
17 #include <unistd.h>
18 #endif
19 #include <algorithm>
20
21 namespace soulng { namespace util {
22
23 std::string GetCurrentWorkingDirectory()
24 {
25 char buf[4096];
26 #ifdef _WIN32
27
28 #elif defined(__linux) || defined(__unix) || defined(__posix)
29
30 #else
31 #error unknown platform
32 #endif
33 if (wd != nullptr)
34 {
35 return Path::MakeCanonical(wd);
36 }
37 else
38 {
39 throw std::runtime_error("could not get current working directory");
40 }
41 }
42
43 bool FileExists(const std::string& filePath)
44 {
45 #ifdef _WIN32
46
47
48
49
50
51
52
53
54
55
56 #elif defined(__linux) || defined(__unix) || defined(__posix)
57
58
59
60
61
62
63
64
65
66
67 #else
68 #error unknown platform
69 #endif
70 }
71
72 bool DirectoryExists(const std::string& directoryPath)
73 {
74 #ifdef _WIN32
75
76
77
78
79
80
81
82
83
84
85 #elif defined(__linux) || defined(__unix) || defined(__posix)
86
87
88
89
90
91
92
93
94
95
96 #else
97 #error unknown platform
98 #endif
99 }
100
101 bool PathExists(const std::string& path)
102 {
103 #ifdef _WIN32
104
105
106 #elif defined(__linux) || defined(__unix) || defined(__posix)
107
108
109 #else
110 #error unknown platform
111 #endif
112 }
113
114 InvalidPathException::InvalidPathException(const std::string& message_): std::runtime_error(message_)
115 {
116 }
117
118 std::string Path::MakeCanonical(const std::string& path)
119 {
120 bool startsWithDriveLetter = false;
121 #ifdef _WIN32
122
123
124
125
126 #endif
127 std::string result;
128 char prev = ' ';
129 bool first = true;
130 for (char c : path)
131 {
132 if (first)
133 {
134 first = false;
135 if (startsWithDriveLetter)
136 {
137 c = std::toupper(static_cast<unsigned char>(c));
138 }
139 }
140 if (c == '\\')
141 {
142 c = '/';
143 }
144 if (c == '/')
145 {
146 if (prev != '/')
147 {
148 result.append(1, c);
149 }
150 }
151 else
152 {
153 result.append(1, c);
154 }
155 prev = c;
156 }
157 if (result.length() == 3 && std::isalpha(result[0]) && result[1] == ':' && result[2] == '/')
158 {
159 return result;
160 }
161 if (result == "/")
162 {
163 return result;
164 }
165 if (!result.empty())
166 {
167 if (result[result.length() - 1] == '/')
168 {
169 return result.substr(0, result.length() - 1);
170 }
171 }
172 return result;
173 }
174
175 std::string Path::ChangeExtension(const std::string& path, const std::string& extension)
176 {
177 std::string::size_type lastDotPos = path.rfind('.');
178 if (extension.empty())
179 {
180 if (lastDotPos != std::string::npos)
181 {
182 return path.substr(0, lastDotPos);
183 }
184 else
185 {
186 return path;
187 }
188 }
189 else
190 {
191 if (lastDotPos == std::string::npos)
192 {
193 if (extension[0] == '.')
194 {
195 return path + extension;
196 }
197 else
198 {
199 return path + "." + extension;
200 }
201 }
202 else
203 {
204 if (extension[0] == '.')
205 {
206 return path.substr(0, lastDotPos) + extension;
207 }
208 else
209 {
210 return path.substr(0, lastDotPos + 1) + extension;
211 }
212 }
213 }
214 }
215
216 bool Path::HasExtension(const std::string& path)
217 {
218 std::string::size_type lastDotPos = path.rfind('.');
219 if (lastDotPos != std::string::npos)
220 {
221 std::string::size_type lastColon = path.find(':', lastDotPos + 1);
222 std::string::size_type lastDirSep = path.find('/', lastDotPos + 1);
223 if (lastColon > lastDotPos || lastDirSep > lastDotPos)
224 {
225 return false;
226 }
227 else if (lastDotPos < path.length() - 1)
228 {
229 return true;
230 }
231 else
232 {
233 return false;
234 }
235 }
236 else
237 {
238 return false;
239 }
240 }
241
242 std::string Path::GetExtension(const std::string& path)
243 {
244 std::string::size_type lastDotPos = path.rfind('.');
245 if (lastDotPos != std::string::npos)
246 {
247 if (path.find('/', lastDotPos + 1) != std::string::npos)
248 {
249 return std::string();
250 }
251 else
252 {
253 return path.substr(lastDotPos);
254 }
255 }
256 else
257 {
258 return std::string();
259 }
260 }
261
262 std::string Path::GetDrive(const std::string& path)
263 {
264 #ifdef _WIN32
265
266
267
268
269
270
271
272 #endif
273 return std::string();
274 }
275
276 std::string Path::GetFileName(const std::string& path)
277 {
278 if (path.empty())
279 {
280 return std::string();
281 }
282 else
283 {
284 char lastChar = path[path.length() - 1];
285 if (lastChar == '/' || lastChar == ':')
286 {
287 return std::string();
288 }
289 else
290 {
291 std::string::size_type lastDirSepPos = path.rfind('/');
292 if (lastDirSepPos != std::string::npos)
293 {
294 return path.substr(lastDirSepPos + 1);
295 }
296 else
297 {
298 return path;
299 }
300 }
301 }
302 }
303
304 std::string Path::GetFileNameWithoutExtension(const std::string& path)
305 {
306 std::string fileName = GetFileName(path);
307 std::string::size_type lastDotPos = fileName.rfind('.');
308 if (lastDotPos != std::string::npos)
309 {
310 return fileName.substr(0, lastDotPos);
311 }
312 else
313 {
314 return fileName;
315 }
316 }
317
318
319 std::string Path::GetDirectoryName(const std::string& path)
320 {
321 if (path.empty())
322 {
323 return std::string();
324 }
325 else if (path.length() == 3 && std::isalpha(path[0]) && path[1] == ':' && path[2] == '/')
326 {
327 return std::string();
328 }
329 else
330 {
331 std::string::size_type lastDirSepPos = path.rfind('/');
332 if (lastDirSepPos != std::string::npos)
333 {
334 return path.substr(0, lastDirSepPos);
335 }
336 else
337 {
338 return std::string();
339 }
340 }
341 }
342
343 std::string Path::Combine(const std::string& path1, const std::string& path2)
344 {
345 if (path1.empty())
346 {
347 return path2;
348 }
349 else if (path2.empty())
350 {
351 return path1;
352 }
353 else
354 {
355 if (IsAbsolute(path2))
356 {
357 return path2;
358 }
359 else
360 {
361 std::string result = path1;
362 if (result[result.length() - 1] != '/')
363 {
364 result.append(1, '/');
365 }
366 result.append(path2);
367 return result;
368 }
369 }
370 }
371
372 bool Path::IsAbsolute(const std::string& path)
373 {
374 if (path.empty())
375 {
376 return false;
377 }
378 else
379 {
380 if (path[0] == '/')
381 {
382 return true;
383 }
384 else if (std::isalpha(path[0]) && path.length() > 2 && path[1] == ':' && path[2] == '/')
385 {
386 return true;
387 }
388 else
389 {
390 return false;
391 }
392 }
393 }
394
395 bool Path::IsRelative(const std::string& path)
396 {
397 return !IsAbsolute(path);
398 }
399
400 std::string GetFullPath(const std::string& path)
401 {
402 std::string p = Path::MakeCanonical(path);
403 if (Path::IsRelative(p))
404 {
405 p = GetCurrentWorkingDirectory();
406 p.append(1, '/');
407 p.append(Path::MakeCanonical(path));
408 }
409 std::vector<std::string> components = Split(p, '/');
410 int w = 0;
411 int n = int(components.size());
412 for (int i = 0; i < n; ++i)
413 {
414 const std::string& c = components[i];
415 if (i == 0 || (!c.empty() && c != "."))
416 {
417 if (c == "..")
418 {
419 --w;
420 if (w < 0)
421 {
422 throw InvalidPathException("path '" + path + "' is invalid");
423 }
424 }
425 else
426 {
427 if (w != i)
428 {
429 components[w] = components[i];
430 }
431 ++w;
432 }
433 }
434 }
435 if (w == 0)
436 {
437 return "/";
438 }
439 else if (w == 1)
440 {
441 const std::string& p = components[0];
442 if (p.length() == 2 && std::isalpha(p[0]) && p[1] == ':')
443 {
444 return p + "/";
445 }
446 }
447 std::string result;
448 for (int i = 0; i < w; ++i)
449 {
450 if (i != 0)
451 {
452 result.append(1, '/');
453 }
454 result.append(components[i]);
455 }
456 return result;
457 }
458
459 std::string MakeRelativeDirPath(const std::string& dirPath, const std::string& referenceDirPath)
460 {
461 std::string p = GetFullPath(dirPath);
462 std::string r = GetFullPath(referenceDirPath);
463 if (p == r)
464 {
465 return std::string();
466 }
467 if (Path::GetDrive(p) != Path::GetDrive(r))
468 {
469 return p;
470 }
471 std::vector<std::string> pc = Split(p, '/');
472 std::vector<std::string> rc = Split(r, '/');
473 int n = std::min(pc.size(), rc.size());
474 int m = 0;
475 for (; m < n; ++m)
476 {
477 if (pc[m] != rc[m])
478 {
479 break;
480 }
481 }
482 std::string result;
483 for (int i = m; i < rc.size(); ++i)
484 {
485 result = Path::Combine(result, "..");
486 }
487 for (int i = m; i < pc.size(); ++i)
488 {
489 result = Path::Combine(result, pc[i]);
490 }
491 return result;
492 }
493
494 } }