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