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
isDirSeparator(int ch,HostType hostType)20 bool PathUtils::isDirSeparator(int ch, HostType hostType) {
21 return (ch == '/') || (hostType == HOST_WIN32 && ch == '\\');
22 }
23
24
25 // static
isPathSeparator(int ch,HostType hostType)26 bool PathUtils::isPathSeparator(int ch, HostType hostType) {
27 return (hostType == HOST_POSIX && ch == ':') ||
28 (hostType == HOST_WIN32 && ch == ';');
29 }
30
31 // static
rootPrefixSize(const char * path,HostType hostType)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
isAbsolute(const char * path,HostType hostType)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
decompose(const char * path,HostType hostType)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
recompose(const StringVector & components,HostType hostType)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
simplifyComponents(StringVector * components)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