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