1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3
4 #include "cmConfigure.h" // IWYU pragma: keep
5
6 #include "cmCMakePath.h"
7
8 #include <string>
9
10 #if defined(_WIN32)
11 # include <cstdlib>
12 #endif
13
14 #include <cm/filesystem>
15 #include <cm/string_view>
16
17 #if defined(_WIN32)
18 # include "cmStringAlgorithms.h"
19 #endif
20
ReplaceWideExtension(cm::string_view extension)21 cmCMakePath& cmCMakePath::ReplaceWideExtension(cm::string_view extension)
22 {
23 auto file = this->Path.filename().string();
24 if (!file.empty() && file != "." && file != "..") {
25 auto pos = file.find('.', file[0] == '.' ? 1 : 0);
26 if (pos != std::string::npos) {
27 file.erase(pos);
28 }
29 }
30 if (!extension.empty()) {
31 if (extension[0] != '.') {
32 file += '.';
33 }
34 file.append(std::string(extension));
35 }
36 this->Path.replace_filename(file);
37 return *this;
38 }
39
GetWideExtension() const40 cmCMakePath cmCMakePath::GetWideExtension() const
41 {
42 auto file = this->Path.filename().string();
43 if (file.empty() || file == "." || file == "..") {
44 return cmCMakePath{};
45 }
46
47 auto pos = file.find('.', file[0] == '.' ? 1 : 0);
48 if (pos != std::string::npos) {
49 return cm::string_view(file.data() + pos, file.length() - pos);
50 }
51
52 return cmCMakePath{};
53 }
54
GetNarrowStem() const55 cmCMakePath cmCMakePath::GetNarrowStem() const
56 {
57 auto stem = this->Path.stem().string();
58 if (!stem.empty()) {
59 auto pos = stem.find('.', stem[0] == '.' ? 1 : 0);
60 if (pos != std::string::npos) {
61 return stem.substr(0, pos);
62 }
63 }
64 return stem;
65 }
66
Absolute(const cm::filesystem::path & base) const67 cmCMakePath cmCMakePath::Absolute(const cm::filesystem::path& base) const
68 {
69 if (this->Path.is_relative()) {
70 auto path = base;
71 path /= this->Path;
72 // filesystem::path::operator/= use preferred_separator ('\' on Windows)
73 // so converts back to '/'
74 return path.generic_string();
75 }
76 return *this;
77 }
78
IsPrefix(const cmCMakePath & path) const79 bool cmCMakePath::IsPrefix(const cmCMakePath& path) const
80 {
81 auto prefix_it = this->Path.begin();
82 auto prefix_end = this->Path.end();
83 auto path_it = path.Path.begin();
84 auto path_end = path.Path.end();
85
86 while (prefix_it != prefix_end && path_it != path_end &&
87 *prefix_it == *path_it) {
88 ++prefix_it;
89 ++path_it;
90 }
91 return (prefix_it == prefix_end) ||
92 (prefix_it->empty() && path_it != path_end);
93 }
94
FormatPath(std::string path,format fmt)95 std::string cmCMakePath::FormatPath(std::string path, format fmt)
96 {
97 #if defined(_WIN32)
98 if (fmt == auto_format || fmt == native_format) {
99 auto prefix = path.substr(0, 4);
100 for (auto& c : prefix) {
101 if (c == '\\') {
102 c = '/';
103 }
104 }
105 // remove Windows long filename marker
106 if (prefix == "//?/"_s) {
107 path.erase(0, 4);
108 }
109 if (cmHasPrefix(path, "UNC/"_s) || cmHasPrefix(path, "UNC\\"_s)) {
110 path.erase(0, 2);
111 path[0] = '/';
112 }
113 }
114 #else
115 static_cast<void>(fmt);
116 #endif
117 return path;
118 }
119
GetNativePath(std::string & path) const120 void cmCMakePath::GetNativePath(std::string& path) const
121 {
122 cm::filesystem::path tmp(this->Path);
123 tmp.make_preferred();
124
125 path = tmp.string();
126 }
GetNativePath(std::wstring & path) const127 void cmCMakePath::GetNativePath(std::wstring& path) const
128 {
129 cm::filesystem::path tmp(this->Path);
130 tmp.make_preferred();
131
132 path = tmp.wstring();
133
134 #if defined(_WIN32)
135 // Windows long filename
136 static std::wstring UNC(L"\\\\?\\UNC");
137 static std::wstring PREFIX(L"\\\\?\\");
138
139 if (this->IsAbsolute() && path.length() > _MAX_PATH - 12) {
140 if (this->HasRootName() && path[0] == L'\\') {
141 path = UNC + path.substr(1);
142 } else {
143 path = PREFIX + path;
144 }
145 }
146 #endif
147 }
148