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 16 namespace android { 17 namespace base { 18 19 // static 20 bool PathUtils::isDirSeparator(int ch, HostType hostType) { 21 return (ch == '/') || (hostType == HOST_WIN32 && ch == '\\'); 22 } 23 24 25 // static 26 bool PathUtils::isPathSeparator(int ch, HostType hostType) { 27 return (hostType == HOST_POSIX && ch == ':') || 28 (hostType == HOST_WIN32 && ch == ';'); 29 } 30 31 // static 32 size_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 63 bool 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 75 StringVector 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 101 String 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 134 void 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