1 // Copyright 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "base/PathUtils.h"
16
17 #include <string.h> // for size_t, strncmp
18 #include <iterator> // for reverse_iterator, operator!=
19 #include <numeric> // for accumulate
20 #include <type_traits> // for enable_if<>::type
21
22 #ifndef _WIN32
23 #include <unistd.h>
24 #endif
25
26 #ifdef _WIN32
27 #include "Win32UnicodeString.h"
28 #endif
29
sIsEmpty(const char * str)30 static inline bool sIsEmpty(const char* str) {
31 return !str || str[0] == '\0';
32 }
33
34 namespace android {
35 namespace base {
36
37 const char* const PathUtils::kExeNameSuffixes[kHostTypeCount] = {"", ".exe"};
38
39 const char* const PathUtils::kExeNameSuffix =
40 PathUtils::kExeNameSuffixes[PathUtils::HOST_TYPE];
41
toExecutableName(const char * baseName,HostType hostType)42 std::string PathUtils::toExecutableName(const char* baseName,
43 HostType hostType) {
44 return static_cast<std::string>(baseName).append(
45 kExeNameSuffixes[hostType]);
46 }
47
48 // static
isDirSeparator(int ch,HostType hostType)49 bool PathUtils::isDirSeparator(int ch, HostType hostType) {
50 return (ch == '/') || (hostType == HOST_WIN32 && ch == '\\');
51 }
52
53 // static
isPathSeparator(int ch,HostType hostType)54 bool PathUtils::isPathSeparator(int ch, HostType hostType) {
55 return (hostType == HOST_POSIX && ch == ':') ||
56 (hostType == HOST_WIN32 && ch == ';');
57 }
58
59 // static
rootPrefixSize(const std::string & path,HostType hostType)60 size_t PathUtils::rootPrefixSize(const std::string& path, HostType hostType) {
61 if (path.empty()) return 0;
62
63 if (hostType != HOST_WIN32)
64 return (path[0] == '/') ? 1U : 0U;
65
66 size_t result = 0;
67 if (path[1] == ':') {
68 int ch = path[0];
69 if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
70 result = 2U;
71 } else if (!strncmp(path.c_str(), "\\\\.\\", 4) ||
72 !strncmp(path.c_str(), "\\\\?\\", 4)) {
73 // UNC prefixes.
74 return 4U;
75 } else if (isDirSeparator(path[0], hostType)) {
76 result = 1;
77 if (isDirSeparator(path[1], hostType)) {
78 result = 2;
79 while (path[result] && !isDirSeparator(path[result], HOST_WIN32))
80 result++;
81 }
82 }
83 if (result && path[result] && isDirSeparator(path[result], HOST_WIN32))
84 result++;
85
86 return result;
87 }
88
89 // static
isAbsolute(const char * path,HostType hostType)90 bool PathUtils::isAbsolute(const char* path, HostType hostType) {
91 size_t prefixSize = rootPrefixSize(path, hostType);
92 if (!prefixSize) {
93 return false;
94 }
95 if (hostType != HOST_WIN32) {
96 return true;
97 }
98 return isDirSeparator(path[prefixSize - 1], HOST_WIN32);
99 }
100
101 // static
removeTrailingDirSeparator(const char * path,HostType hostType)102 std::string PathUtils::removeTrailingDirSeparator(const char* path,
103 HostType hostType) {
104 size_t pathLen = strlen(path);
105 // NOTE: Don't remove initial path separator for absolute paths.
106 while (pathLen > 1U && isDirSeparator(path[pathLen - 1U], hostType)) {
107 pathLen--;
108 }
109 return std::string(path, pathLen);
110 }
111
112 // static
addTrailingDirSeparator(const char * path,HostType hostType)113 std::string PathUtils::addTrailingDirSeparator(const char* path,
114 HostType hostType) {
115 std::string result = path;
116 if (result.size() > 0 && !isDirSeparator(result[result.size() - 1U])) {
117 result += getDirSeparator(hostType);
118 }
119 return result;
120 }
121
122 // static
split(const char * path,HostType hostType,const char ** dirName,const char ** baseName)123 bool PathUtils::split(const char* path,
124 HostType hostType,
125 const char** dirName,
126 const char** baseName) {
127 if (sIsEmpty(path)) {
128 return false;
129 }
130
131 // If there is a trailing directory separator, return an error.
132 size_t end = strlen(path);
133 if (isDirSeparator(path[end - 1U], hostType)) {
134 return false;
135 }
136
137 // Find last separator.
138 size_t prefixLen = rootPrefixSize(path, hostType);
139 size_t pos = end;
140 while (pos > prefixLen && !isDirSeparator(path[pos - 1U], hostType)) {
141 pos--;
142 }
143
144 // Handle common case.
145 if (pos > prefixLen) {
146 if (dirName) {
147 *dirName = path;
148 }
149 if (baseName) {
150 *baseName = path + pos;
151 }
152 return true;
153 }
154
155 // If there is no directory separator, the path is a single file name.
156 if (dirName) {
157 if (!prefixLen) {
158 *dirName = ".";
159 } else {
160 *dirName = path;
161 }
162 }
163 if (baseName) {
164 *baseName = path + prefixLen;
165 }
166 return true;
167 }
168
169 // static
join(const std::string & path1,const std::string & path2,HostType hostType)170 std::string PathUtils::join(const std::string& path1,
171 const std::string& path2,
172 HostType hostType) {
173 if (path1.empty()) {
174 return path2;
175 }
176 if (path2.empty()) {
177 return path1;
178 }
179 if (isAbsolute(path2.c_str(), hostType)) {
180 return path2;
181 }
182 size_t prefixLen = rootPrefixSize(path1, hostType);
183 std::string result(path1);
184 size_t end = result.size();
185 if (end > prefixLen && !isDirSeparator(result[end - 1U], hostType)) {
186 result += getDirSeparator(hostType);
187 }
188 result += path2;
189 return result;
190 }
191
192 // static
193 template <class String>
decompose(const String & path,HostType hostType)194 std::vector<String> PathUtils::decompose(const String& path,
195 HostType hostType) {
196 std::vector<String> result;
197 if (path.empty())
198 return result;
199
200 size_t prefixLen = rootPrefixSize(path, hostType);
201 auto it = path.begin();
202 if (prefixLen) {
203 result.emplace_back(it, it + prefixLen);
204 it += prefixLen;
205 }
206 for (;;) {
207 auto p = it;
208 while (p != path.end() && !isDirSeparator(*p, hostType))
209 p++;
210 if (p > it) {
211 result.emplace_back(it, p);
212 }
213 if (p == path.end()) {
214 break;
215 }
216 it = p + 1;
217 }
218 return result;
219 }
220
decompose(std::string && path,HostType hostType)221 std::vector<std::string> PathUtils::decompose(std::string&& path,
222 HostType hostType) {
223 return decompose<std::string>(path, hostType);
224 }
225
decompose(const std::string & path,HostType hostType)226 std::vector<std::string> PathUtils::decompose(const std::string& path,
227 HostType hostType) {
228 return decompose<std::string>(path, hostType);
229 }
230
231 template <class String>
recompose(const std::vector<String> & components,HostType hostType)232 std::string PathUtils::recompose(const std::vector<String>& components,
233 HostType hostType) {
234 if (components.empty()) {
235 return {};
236 }
237
238 const char dirSeparator = getDirSeparator(hostType);
239 std::string result;
240 // To reduce memory allocations, compute capacity before doing the
241 // real append.
242 const size_t capacity =
243 components.size() - 1 +
244 std::accumulate(components.begin(), components.end(), size_t(0),
245 [](size_t val, const String& next) {
246 return val + next.size();
247 });
248
249 result.reserve(capacity);
250 bool addSeparator = false;
251 for (size_t n = 0; n < components.size(); ++n) {
252 const auto& component = components[n];
253 if (addSeparator)
254 result += dirSeparator;
255 addSeparator = true;
256 if (n == 0) {
257 size_t prefixLen = rootPrefixSize(component, hostType);
258 if (prefixLen == component.size()) {
259 addSeparator = false;
260 }
261 }
262 result += component;
263 }
264 return result;
265 }
266
267 // static
recompose(const std::vector<std::string> & components,HostType hostType)268 std::string PathUtils::recompose(const std::vector<std::string>& components,
269 HostType hostType) {
270 return recompose<std::string>(components, hostType);
271 }
272
273 // static
274 template <class String>
simplifyComponents(std::vector<String> * components)275 void PathUtils::simplifyComponents(std::vector<String>* components) {
276 std::vector<String> stack;
277 for (auto& component : *components) {
278 if (component == ".") {
279 // Ignore any instance of '.' from the list.
280 continue;
281 }
282 if (component == "..") {
283 // Handling of '..' is specific: if there is a item on the
284 // stack that is not '..', then remove it, otherwise push
285 // the '..'.
286 if (!stack.empty() && stack.back() != "..") {
287 stack.pop_back();
288 } else {
289 stack.push_back(std::move(component));
290 }
291 continue;
292 }
293 // If not a '..', just push on the stack.
294 stack.push_back(std::move(component));
295 }
296 if (stack.empty()) {
297 stack.push_back(".");
298 }
299 components->swap(stack);
300 }
301
simplifyComponents(std::vector<std::string> * components)302 void PathUtils::simplifyComponents(std::vector<std::string>* components) {
303 simplifyComponents<std::string>(components);
304 }
305
306 #ifdef _WIN32
307
308 // Return |path| as a Unicode string, while discarding trailing separators.
win32Path(const char * path)309 Win32UnicodeString win32Path(const char* path) {
310 Win32UnicodeString wpath(path);
311 // Get rid of trailing directory separators, Windows doesn't like them.
312 size_t size = wpath.size();
313 while (size > 0U &&
314 (wpath[size - 1U] == L'\\' || wpath[size - 1U] == L'/')) {
315 size--;
316 }
317 if (size < wpath.size()) {
318 wpath.resize(size);
319 }
320 return wpath;
321 }
322
323 /* access function */
324 #define F_OK 0 /* test for existence of file */
325 #define X_OK 0x01 /* test for execute or search permission */
326 #define W_OK 0x02 /* test for write permission */
327 #define R_OK 0x04 /* test for read permission */
328
GetWin32Mode(int mode)329 static int GetWin32Mode(int mode) {
330 // Convert |mode| to win32 permission bits.
331 int win32mode = 0x0;
332
333 if ((mode & R_OK) || (mode & X_OK)) {
334 win32mode |= 0x4;
335 }
336 if (mode & W_OK) {
337 win32mode |= 0x2;
338 }
339
340 return win32mode;
341 }
342
343 #endif
344
pathExists(const char * path)345 bool pathExists(const char* path) {
346 #ifdef _WIN32
347 return _waccess(win32Path(path).c_str(), GetWin32Mode(F_OK));
348 #else
349 return 0 == access(path, F_OK);
350 #endif
351 }
352
pj(const std::vector<std::string> & paths)353 std::string pj(const std::vector<std::string>& paths) {
354 std::string res;
355
356 if (paths.size() == 0)
357 return "";
358
359 if (paths.size() == 1)
360 return paths[0];
361
362 res = paths[0];
363
364 for (size_t i = 1; i < paths.size(); i++) {
365 res = PathUtils::join(res, paths[i]);
366 }
367
368 return res;
369 }
370
addTrailingDirSeparator(const std::string & path,HostType hostType)371 std::string PathUtils::addTrailingDirSeparator(const std::string& path,
372 HostType hostType) {
373 std::string result = path;
374 if (result.size() > 0 && !isDirSeparator(result[result.size() - 1U])) {
375 result += getDirSeparator(hostType);
376 }
377 return result;
378 }
379
380 } // namespace base
381 } // namespace android
382