blob: c529d61abd150e8c3e4ff2e0a3c4a3d267218eb4 [file] [log] [blame]
Songchun Fan3c82a302019-11-29 14:23:45 -08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "path.h"
18
19#include <android-base/strings.h>
20#include <android-base/logging.h>
21
22#include <algorithm>
23#include <iterator>
24#include <limits>
25#include <memory>
26
27#include <dirent.h>
28#include <stdlib.h>
29#include <sys/types.h>
30#include <unistd.h>
31
32using namespace std::literals;
33
34namespace android::incremental::path {
35
36bool PathCharsLess::operator()(char l, char r) const {
37 int ll = l == '/' ? std::numeric_limits<char>::min() - 1 : l;
38 int rr = r == '/' ? std::numeric_limits<char>::min() - 1 : r;
39 return ll < rr;
40}
41
42bool PathLess::operator()(std::string_view l, std::string_view r) const {
43 return std::lexicographical_compare(std::begin(l), std::end(l), std::begin(r), std::end(r),
44 PathCharsLess());
45}
46
47void details::append_next_path(std::string& target, std::string_view path) {
48 if (path.empty()) {
49 return;
50 }
51 if (!target.empty()) {
52 target.push_back('/');
53 }
54 target += path;
55}
56
57bool isAbsolute(std::string_view path) {
58 return !path.empty() && path[0] == '/';
59}
60
61std::string normalize(std::string_view path) {
62 if (path.empty()) {
63 return {};
64 }
65 if (path.starts_with("../"sv)) {
66 return {};
67 }
68
69 std::string result;
70 if (isAbsolute(path)) {
71 path.remove_prefix(1);
72 } else {
73 char buffer[PATH_MAX];
74 if (!::getcwd(buffer, sizeof(buffer))) {
75 return {};
76 }
77 result += buffer;
78 }
79
80 size_t start = 0;
81 size_t end = 0;
82 for (; end != path.npos; start = end + 1) {
83 end = path.find('/', start);
84 // Next component, excluding the separator
85 auto part = path.substr(start, end - start);
86 if (part.empty() || part == "."sv) {
87 continue;
88 }
89 if (part == ".."sv) {
90 if (result.empty()) {
91 return {};
92 }
93 auto lastPos = result.rfind('/');
94 if (lastPos == result.npos) {
95 result.clear();
96 } else {
97 result.resize(lastPos);
98 }
99 continue;
100 }
101 result += '/';
102 result += part;
103 }
104
105 return result;
106}
107
108std::string_view basename(std::string_view path) {
109 if (path.empty()) {
110 return {};
111 }
112 if (path == "/"sv) {
113 return "/"sv;
114 }
115 auto pos = path.rfind('/');
116 while (!path.empty() && pos == path.size() - 1) {
117 path.remove_suffix(1);
118 pos = path.rfind('/');
119 }
120 if (pos == path.npos) {
121 return path.empty() ? "/"sv : path;
122 }
123 return path.substr(pos + 1);
124}
125
126std::string_view dirname(std::string_view path) {
127 if (path.empty()) {
128 return {};
129 }
130 if (path == "/"sv) {
131 return "/"sv;
132 }
133 const auto pos = path.rfind('/');
134 if (pos == 0) {
135 return "/"sv;
136 }
137 if (pos == path.npos) {
138 return "."sv;
139 }
140 return path.substr(0, pos);
141}
142
143details::CStrWrapper::CStrWrapper(std::string_view sv) {
144 if (sv[sv.size()] == '\0') {
145 mCstr = sv.data();
146 } else {
147 mCopy.emplace(sv);
148 mCstr = mCopy->c_str();
149 }
150}
151
152std::optional<bool> isEmptyDir(std::string_view dir) {
153 const auto d = std::unique_ptr<DIR, decltype(&::closedir)>{::opendir(c_str(dir)), ::closedir};
154 if (!d) {
155 if (errno == EPERM || errno == EACCES) {
156 return std::nullopt;
157 }
158 return false;
159 }
160 while (auto entry = ::readdir(d.get())) {
161 if (entry->d_type != DT_DIR) {
162 return false;
163 }
164 if (entry->d_name != "."sv && entry->d_name != ".."sv) {
165 return false;
166 }
167 }
168 return true;
169}
170
171bool startsWith(std::string_view path, std::string_view prefix) {
172 if (!base::StartsWith(path, prefix)) {
173 return false;
174 }
175 return path.size() == prefix.size() || path[prefix.size()] == '/';
176}
177
178} // namespace android::incremental::path