| // Copyright 2014 The Android Open Source Project |
| // |
| // This software is licensed under the terms of the GNU General Public |
| // License version 2, as published by the Free Software Foundation, and |
| // may be copied, distributed, and modified under those terms. |
| // |
| // This program is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| |
| #include "android/base/files/PathUtils.h" |
| |
| #include <string.h> |
| |
| namespace android { |
| namespace base { |
| |
| // static |
| bool PathUtils::isDirSeparator(int ch, HostType hostType) { |
| return (ch == '/') || (hostType == HOST_WIN32 && ch == '\\'); |
| } |
| |
| |
| // static |
| bool PathUtils::isPathSeparator(int ch, HostType hostType) { |
| return (hostType == HOST_POSIX && ch == ':') || |
| (hostType == HOST_WIN32 && ch == ';'); |
| } |
| |
| // static |
| size_t PathUtils::rootPrefixSize(const char* path, HostType hostType) { |
| if (!path || !path[0]) |
| return 0; |
| |
| if (hostType != HOST_WIN32) |
| return (path[0] == '/') ? 1U : 0U; |
| |
| size_t result = 0; |
| if (path[1] == ':') { |
| int ch = path[0]; |
| if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) |
| result = 2U; |
| } else if (!strncmp(path, "\\\\.\\", 4) || |
| !strncmp(path, "\\\\?\\", 4)) { |
| // UNC prefixes. |
| return 4U; |
| } else if (isDirSeparator(path[0], hostType)) { |
| result = 1; |
| if (isDirSeparator(path[1], hostType)) { |
| result = 2; |
| while (path[result] && !isDirSeparator(path[result], HOST_WIN32)) |
| result++; |
| } |
| } |
| if (result && path[result] && isDirSeparator(path[result], HOST_WIN32)) |
| result++; |
| |
| return result; |
| } |
| |
| // static |
| bool PathUtils::isAbsolute(const char* path, HostType hostType) { |
| size_t prefixSize = rootPrefixSize(path, hostType); |
| if (!prefixSize) { |
| return false; |
| } |
| if (hostType != HOST_WIN32) { |
| return true; |
| } |
| return isDirSeparator(path[prefixSize - 1], HOST_WIN32); |
| } |
| |
| // static |
| StringVector PathUtils::decompose(const char* path, HostType hostType) { |
| StringVector result; |
| if (!path || !path[0]) |
| return result; |
| |
| size_t prefixLen = rootPrefixSize(path, hostType); |
| if (prefixLen) { |
| result.push_back(String(path, prefixLen)); |
| path += prefixLen; |
| } |
| for (;;) { |
| const char* p = path; |
| while (*p && !isDirSeparator(*p, hostType)) |
| p++; |
| if (p > path) { |
| result.push_back(String(path, p - path)); |
| } |
| if (!*p) { |
| break; |
| } |
| path = p + 1; |
| } |
| return result; |
| } |
| |
| // static |
| String PathUtils::recompose(const StringVector& components, |
| HostType hostType) { |
| const char dirSeparator = (hostType == HOST_WIN32) ? '\\' : '/'; |
| String result; |
| size_t capacity = 0; |
| // To reduce memory allocations, compute capacity before doing the |
| // real append. |
| for (size_t n = 0; n < components.size(); ++n) { |
| if (n) |
| capacity++; |
| capacity += components[n].size(); |
| } |
| |
| result.reserve(capacity); |
| |
| bool addSeparator = false; |
| for (size_t n = 0; n < components.size(); ++n) { |
| const String& component = components[n]; |
| if (addSeparator) |
| result += dirSeparator; |
| addSeparator = true; |
| if (n == 0) { |
| size_t prefixLen = rootPrefixSize(component.c_str(), hostType); |
| if (prefixLen > 0) { |
| addSeparator = false; |
| } |
| } |
| result += components[n]; |
| } |
| return result; |
| } |
| |
| // static |
| void PathUtils::simplifyComponents(StringVector* components) { |
| StringVector stack; |
| for (StringVector::const_iterator it = components->begin(); |
| it != components->end(); |
| ++it) { |
| if (*it == ".") { |
| // Ignore any instance of '.' from the list. |
| continue; |
| } |
| if (*it == "..") { |
| // Handling of '..' is specific: if there is a item on the |
| // stack that is not '..', then remove it, otherwise push |
| // the '..'. |
| if (!stack.empty() && stack[stack.size() -1U] != "..") { |
| stack.resize(stack.size() - 1U); |
| } else { |
| stack.push_back(*it); |
| } |
| continue; |
| } |
| // If not a '..', just push on the stack. |
| stack.push_back(*it); |
| } |
| if (stack.empty()) |
| stack.push_back("."); |
| |
| components->swap(&stack); |
| } |
| |
| } // namespace base |
| } // namespace android |