// 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