• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "aemu/base/files/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 "aemu/base/system/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
extension(const std::string & path,HostType hostType)102 std::string_view PathUtils::extension(const std::string& path,
103                                       HostType hostType) {
104     std::string_view tmp = path;
105     using riter = std::reverse_iterator<std::string_view::const_iterator>;
106 
107     for (auto it = riter(tmp.end()), itEnd = riter(tmp.begin()); it != itEnd;
108          ++it) {
109         if (*it == '.') {
110             // reverse iterator stores a base+1, so decrement it when returning
111             // MSVC doesn't have string_view constructor with iterators
112             return std::string_view(&*std::prev(it.base()), (it - riter(tmp.end()) + 1));
113         }
114         if (isDirSeparator(*it, hostType)) {
115             // no extension here - we've found the end of file name already
116             break;
117         }
118     }
119 
120     // either there's no dot in the whole path, or we found directory separator
121     // first - anyway, there's no extension in this name
122     return "";
123 }
124 // static
removeTrailingDirSeparator(const char * path,HostType hostType)125 std::string PathUtils::removeTrailingDirSeparator(const char* path,
126                                                  HostType hostType) {
127     size_t pathLen = strlen(path);
128     // NOTE: Don't remove initial path separator for absolute paths.
129     while (pathLen > 1U && isDirSeparator(path[pathLen - 1U], hostType)) {
130         pathLen--;
131     }
132     return std::string(path, pathLen);
133 }
134 
135 // static
addTrailingDirSeparator(const char * path,HostType hostType)136 std::string PathUtils::addTrailingDirSeparator(const char* path,
137                                                HostType hostType) {
138     std::string result = path;
139     if (result.size() > 0 && !isDirSeparator(result[result.size() - 1U])) {
140         result += getDirSeparator(hostType);
141     }
142     return result;
143 }
144 
145 // static
split(const char * path,HostType hostType,std::string * dirName,std::string * baseName)146 bool PathUtils::split(const char* path,
147                       HostType hostType,
148                       std::string* dirName,
149                       std::string* baseName) {
150     if (sIsEmpty(path)) {
151         return false;
152     }
153 
154     // If there is a trailing directory separator, return an error.
155     size_t end = strlen(path);
156     if (isDirSeparator(path[end - 1U], hostType)) {
157         return false;
158     }
159 
160     // Find last separator.
161     size_t prefixLen = rootPrefixSize(path, hostType);
162     size_t pos = end;
163     while (pos > prefixLen && !isDirSeparator(path[pos - 1U], hostType)) {
164         pos--;
165     }
166 
167     // Handle common case.
168     if (pos > prefixLen) {
169         if (dirName) {
170             *dirName = std::string(path, pos);
171         }
172         if (baseName) {
173             *baseName = path + pos;
174         }
175         return true;
176     }
177 
178     // If there is no directory separator, the path is a single file name.
179     if (dirName) {
180         if (!prefixLen) {
181             *dirName = ".";
182         } else {
183             *dirName = std::string(path, prefixLen);
184         }
185     }
186     if (baseName) {
187         *baseName = path + prefixLen;
188     }
189     return true;
190 }
191 
192 // static
join(const std::string & path1,const std::string & path2,HostType hostType)193 std::string PathUtils::join(const std::string& path1,
194                             const std::string& path2,
195                             HostType hostType) {
196     if (path1.empty()) {
197         return path2;
198     }
199     if (path2.empty()) {
200         return path1;
201     }
202     if (isAbsolute(path2.c_str(), hostType)) {
203         return path2;
204     }
205     size_t prefixLen = rootPrefixSize(path1, hostType);
206     std::string result(path1);
207     size_t end = result.size();
208     if (end > prefixLen && !isDirSeparator(result[end - 1U], hostType)) {
209         result += getDirSeparator(hostType);
210     }
211     result += path2;
212     return result;
213 }
214 
215 template <class String>
decomposeImpl(const String & path,PathUtils::HostType hostType)216 static std::vector<String> decomposeImpl(const String& path, PathUtils::HostType hostType) {
217     std::vector<String> result;
218     if (path.empty())
219         return result;
220 
221     size_t prefixLen = PathUtils::rootPrefixSize(path, hostType);
222     auto it = path.begin();
223     if (prefixLen) {
224         result.emplace_back(it, it + prefixLen);
225         it += prefixLen;
226     }
227     for (;;) {
228         auto p = it;
229         while (p != path.end() && !PathUtils::isDirSeparator(*p, hostType))
230             p++;
231         if (p > it) {
232             result.emplace_back(it, p);
233         }
234         if (p == path.end()) {
235             break;
236         }
237         it = p + 1;
238     }
239     return result;
240 }
241 
decompose(std::string && path,HostType hostType)242 std::vector<std::string> PathUtils::decompose(std::string&& path,
243                                               HostType hostType) {
244     return decomposeImpl<std::string>(path, hostType);
245 }
246 
decompose(const std::string & path,HostType hostType)247 std::vector<std::string> PathUtils::decompose(const std::string& path,
248                                               HostType hostType) {
249     return decomposeImpl<std::string>(path, hostType);
250 }
251 
252 template <class String>
recompose(const std::vector<String> & components,HostType hostType)253 std::string PathUtils::recompose(const std::vector<String>& components,
254                                  HostType hostType) {
255     if (components.empty()) {
256         return {};
257     }
258 
259     const char dirSeparator = getDirSeparator(hostType);
260     std::string result;
261     // To reduce memory allocations, compute capacity before doing the
262     // real append.
263     const size_t capacity =
264             components.size() - 1 +
265             std::accumulate(components.begin(), components.end(), size_t(0),
266                             [](size_t val, const String& next) {
267                                 return val + next.size();
268                             });
269 
270     result.reserve(capacity);
271     bool addSeparator = false;
272     for (size_t n = 0; n < components.size(); ++n) {
273         const auto& component = components[n];
274         if (addSeparator)
275             result += dirSeparator;
276         addSeparator = true;
277         if (n == 0) {
278             size_t prefixLen = rootPrefixSize(component, hostType);
279             if (prefixLen == component.size()) {
280                 addSeparator = false;
281             }
282         }
283         result += component;
284     }
285     return result;
286 }
287 
288 // static
recompose(const std::vector<std::string> & components,HostType hostType)289 std::string PathUtils::recompose(const std::vector<std::string>& components,
290                                  HostType hostType) {
291     return recompose<std::string>(components, hostType);
292 }
293 
294 // static
295 template <class String>
simplifyComponents(std::vector<String> * components)296 void PathUtils::simplifyComponents(std::vector<String>* components) {
297     std::vector<String> stack;
298     for (auto& component : *components) {
299         if (component == ".") {
300             // Ignore any instance of '.' from the list.
301             continue;
302         }
303         if (component == "..") {
304             // Handling of '..' is specific: if there is a item on the
305             // stack that is not '..', then remove it, otherwise push
306             // the '..'.
307             if (!stack.empty() && stack.back() != "..") {
308                 stack.pop_back();
309             } else {
310                 stack.push_back(std::move(component));
311             }
312             continue;
313         }
314         // If not a '..', just push on the stack.
315         stack.push_back(std::move(component));
316     }
317     if (stack.empty()) {
318         stack.push_back(".");
319     }
320     components->swap(stack);
321 }
322 
simplifyComponents(std::vector<std::string> * components)323 void PathUtils::simplifyComponents(std::vector<std::string>* components) {
324     simplifyComponents<std::string>(components);
325 }
326 
327 // static
relativeTo(const std::string & base,const std::string & path,HostType hostType)328 std::string PathUtils::relativeTo(const std::string& base,
329                                   const std::string& path,
330                                   HostType hostType) {
331     auto baseDecomposed = decompose(base, hostType);
332     auto pathDecomposed = decompose(path, hostType);
333 
334     if (baseDecomposed.size() > pathDecomposed.size())
335         return path;
336 
337     for (size_t i = 0; i < baseDecomposed.size(); i++) {
338         if (baseDecomposed[i] != pathDecomposed[i])
339             return path;
340     }
341 
342     std::string result =
343             recompose(std::vector<std::string>(
344                               pathDecomposed.begin() + baseDecomposed.size(),
345                               pathDecomposed.end()),
346                       hostType);
347 
348     return result;
349 }
350 
move(const std::string & from,const std::string & to)351 bool PathUtils::move(const std::string& from, const std::string& to) {
352     // std::rename returns 0 on success.
353     if (std::rename(from.data(), to.data())) {
354 #ifdef _SUPPORT_FILESYSTEM
355         // Rename can fail if files are on different disks
356         if (std::filesystem::copy_file(from.data(), to.data())) {
357             std::filesystem::remove(from.data());
358             return true;
359         } else {
360             return false;
361         }
362 #else   // _SUPPORT_FILESYSTEM
363         return false;
364 #endif  // _SUPPORT_FILESYSTEM
365     }
366     return true;
367 }
368 
369 #ifdef _WIN32
370 
371 // Return |path| as a Unicode string, while discarding trailing separators.
win32Path(const char * path)372 Win32UnicodeString win32Path(const char* path) {
373     Win32UnicodeString wpath(path);
374     // Get rid of trailing directory separators, Windows doesn't like them.
375     size_t size = wpath.size();
376     while (size > 0U &&
377            (wpath[size - 1U] == L'\\' || wpath[size - 1U] == L'/')) {
378         size--;
379     }
380     if (size < wpath.size()) {
381         wpath.resize(size);
382     }
383     return wpath;
384 }
385 
386 /* access function */
387 #define	F_OK		0	/* test for existence of file */
388 #define	X_OK		0x01	/* test for execute or search permission */
389 #define	W_OK		0x02	/* test for write permission */
390 #define	R_OK		0x04	/* test for read permission */
391 
GetWin32Mode(int mode)392 static int GetWin32Mode(int mode) {
393     // Convert |mode| to win32 permission bits.
394     int win32mode = 0x0;
395 
396     if ((mode & R_OK) || (mode & X_OK)) {
397         win32mode |= 0x4;
398     }
399     if (mode & W_OK) {
400         win32mode |= 0x2;
401     }
402 
403     return win32mode;
404 }
405 
406 #endif
407 
pathExists(const char * path)408 bool pathExists(const char* path) {
409 #ifdef _WIN32
410     return _waccess(win32Path(path).c_str(), GetWin32Mode(F_OK));
411 #else
412     return 0 == access(path, F_OK);
413 #endif
414 }
415 
pj(const std::string & path1,const std::string & path2)416 std::string pj(const std::string& path1, const std::string& path2) {
417     return PathUtils::join(path1, path2);
418 }
419 
pj(const std::vector<std::string> & paths)420 std::string pj(const std::vector<std::string>& paths) {
421     std::string res;
422 
423     if (paths.size() == 0)
424         return "";
425 
426     if (paths.size() == 1)
427         return paths[0];
428 
429     res = paths[0];
430 
431     for (size_t i = 1; i < paths.size(); i++) {
432         res = PathUtils::join(res, paths[i]);
433     }
434 
435     return res;
436 }
437 
addTrailingDirSeparator(const std::string & path,HostType hostType)438 std::string PathUtils::addTrailingDirSeparator(const std::string& path,
439                                                HostType hostType) {
440     std::string result = path;
441     if (result.size() > 0 && !isDirSeparator(result[result.size() - 1U])) {
442         result += getDirSeparator(hostType);
443     }
444     return result;
445 }
446 
447 }  // namespace base
448 }  // namespace android
449