• Home
  • 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 "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