1// Copyright 2014 The Android Open Source Project 2// 3// This software is licensed under the terms of the GNU General Public 4// License version 2, as published by the Free Software Foundation, and 5// may be copied, distributed, and modified under those terms. 6// 7// This program is distributed in the hope that it will be useful, 8// but WITHOUT ANY WARRANTY; without even the implied warranty of 9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10// GNU General Public License for more details. 11 12#include "android/base/files/PathUtils.h" 13 14#include <string.h> 15 16namespace android { 17namespace base { 18 19// static 20bool PathUtils::isDirSeparator(int ch, HostType hostType) { 21 return (ch == '/') || (hostType == HOST_WIN32 && ch == '\\'); 22} 23 24 25// static 26bool PathUtils::isPathSeparator(int ch, HostType hostType) { 27 return (hostType == HOST_POSIX && ch == ':') || 28 (hostType == HOST_WIN32 && ch == ';'); 29} 30 31// static 32size_t PathUtils::rootPrefixSize(const char* path, HostType hostType) { 33 if (!path || !path[0]) 34 return 0; 35 36 if (hostType != HOST_WIN32) 37 return (path[0] == '/') ? 1U : 0U; 38 39 size_t result = 0; 40 if (path[1] == ':') { 41 int ch = path[0]; 42 if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) 43 result = 2U; 44 } else if (!strncmp(path, "\\\\.\\", 4) || 45 !strncmp(path, "\\\\?\\", 4)) { 46 // UNC prefixes. 47 return 4U; 48 } else if (isDirSeparator(path[0], hostType)) { 49 result = 1; 50 if (isDirSeparator(path[1], hostType)) { 51 result = 2; 52 while (path[result] && !isDirSeparator(path[result], HOST_WIN32)) 53 result++; 54 } 55 } 56 if (result && path[result] && isDirSeparator(path[result], HOST_WIN32)) 57 result++; 58 59 return result; 60} 61 62// static 63bool PathUtils::isAbsolute(const char* path, HostType hostType) { 64 size_t prefixSize = rootPrefixSize(path, hostType); 65 if (!prefixSize) { 66 return false; 67 } 68 if (hostType != HOST_WIN32) { 69 return true; 70 } 71 return isDirSeparator(path[prefixSize - 1], HOST_WIN32); 72} 73 74// static 75StringVector PathUtils::decompose(const char* path, HostType hostType) { 76 StringVector result; 77 if (!path || !path[0]) 78 return result; 79 80 size_t prefixLen = rootPrefixSize(path, hostType); 81 if (prefixLen) { 82 result.push_back(String(path, prefixLen)); 83 path += prefixLen; 84 } 85 for (;;) { 86 const char* p = path; 87 while (*p && !isDirSeparator(*p, hostType)) 88 p++; 89 if (p > path) { 90 result.push_back(String(path, p - path)); 91 } 92 if (!*p) { 93 break; 94 } 95 path = p + 1; 96 } 97 return result; 98} 99 100// static 101String PathUtils::recompose(const StringVector& components, 102 HostType hostType) { 103 const char dirSeparator = (hostType == HOST_WIN32) ? '\\' : '/'; 104 String result; 105 size_t capacity = 0; 106 // To reduce memory allocations, compute capacity before doing the 107 // real append. 108 for (size_t n = 0; n < components.size(); ++n) { 109 if (n) 110 capacity++; 111 capacity += components[n].size(); 112 } 113 114 result.reserve(capacity); 115 116 bool addSeparator = false; 117 for (size_t n = 0; n < components.size(); ++n) { 118 const String& component = components[n]; 119 if (addSeparator) 120 result += dirSeparator; 121 addSeparator = true; 122 if (n == 0) { 123 size_t prefixLen = rootPrefixSize(component.c_str(), hostType); 124 if (prefixLen > 0) { 125 addSeparator = false; 126 } 127 } 128 result += components[n]; 129 } 130 return result; 131} 132 133// static 134void PathUtils::simplifyComponents(StringVector* components) { 135 StringVector stack; 136 for (StringVector::const_iterator it = components->begin(); 137 it != components->end(); 138 ++it) { 139 if (*it == ".") { 140 // Ignore any instance of '.' from the list. 141 continue; 142 } 143 if (*it == "..") { 144 // Handling of '..' is specific: if there is a item on the 145 // stack that is not '..', then remove it, otherwise push 146 // the '..'. 147 if (!stack.empty() && stack[stack.size() -1U] != "..") { 148 stack.resize(stack.size() - 1U); 149 } else { 150 stack.push_back(*it); 151 } 152 continue; 153 } 154 // If not a '..', just push on the stack. 155 stack.push_back(*it); 156 } 157 if (stack.empty()) 158 stack.push_back("."); 159 160 components->swap(&stack); 161} 162 163} // namespace base 164} // namespace android 165