Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
path.cc
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#include "ortools/base/path.h"
15
16#include <cstring>
17#include <string>
18
19#include "absl/strings/str_cat.h"
20
21namespace file {
22
23// 40% of the time in JoinPath() is from calls with 2 arguments, so we
24// specialize that case.
25std::string JoinPath(absl::string_view path1, absl::string_view path2) {
26 if (path1.empty()) return std::string(path2);
27 if (path2.empty()) return std::string(path1);
28 if (path1.back() == '/') {
29 if (path2.front() == '/')
30 return absl::StrCat(path1, absl::ClippedSubstr(path2, 1));
31 } else {
32 if (path2.front() != '/') return absl::StrCat(path1, "/", path2);
33 }
34 return absl::StrCat(path1, path2);
35}
36
37namespace internal {
38
39// Given a collection of file paths, append them all together,
40// ensuring that the proper path separators are inserted between them.
41std::string JoinPathImpl(bool honor_abs,
42 std::initializer_list<absl::string_view> paths) {
43 std::string result;
44
45 if (paths.size() != 0) {
46 // This size calculation is worst-case: it assumes one extra "/" for every
47 // path other than the first.
48 size_t total_size = paths.size() - 1;
49 for (const absl::string_view path : paths) total_size += path.size();
50 result.resize(total_size);
51
52 auto begin = result.begin();
53 auto out = begin;
54 bool trailing_slash = false;
55 for (absl::string_view path : paths) {
56 if (path.empty()) continue;
57 if (path.front() == '/') {
58 if (honor_abs) {
59 out = begin; // wipe out whatever we've built up so far.
60 } else if (trailing_slash) {
61 path.remove_prefix(1);
62 }
63 } else {
64 if (!trailing_slash && out != begin) *out++ = '/';
65 }
66 const size_t this_size = path.size();
67 memcpy(&*out, path.data(), this_size);
68 out += this_size;
69 trailing_slash = out[-1] == '/';
70 }
71 result.erase(out - begin);
72 }
73 return result;
74}
75
76// Return the parts of the basename of path, split on the final ".".
77// If there is no "." in the basename or "." is the final character in the
78// basename, the second value will be empty.
79std::pair<absl::string_view, absl::string_view> SplitBasename(
80 absl::string_view path) {
81 path = Basename(path);
82
83 size_t pos = path.find_last_of('.');
84 if (pos == absl::string_view::npos)
85 return std::make_pair(path, absl::ClippedSubstr(path, path.size(), 0));
86 return std::make_pair(path.substr(0, pos),
87 absl::ClippedSubstr(path, pos + 1));
88}
89
90} // namespace internal
91
92bool IsAbsolutePath(absl::string_view path) {
93 return !path.empty() && path[0] == '/';
94}
95
96std::string AddSlash(absl::string_view path) {
97 size_t length = path.size();
98 if (length && path[length - 1] != '/') {
99 return absl::StrCat(path, "/");
100 } else {
101 return std::string(path);
102 }
103}
104
105absl::string_view Dirname(absl::string_view path) {
106 return SplitPath(path).first;
107}
108
109absl::string_view Basename(absl::string_view path) {
110 return SplitPath(path).second;
111}
112
113std::pair<absl::string_view, absl::string_view> SplitPath(
114 absl::string_view path) {
115 size_t pos = path.find_last_of('/');
116
117 // Handle the case with no '/' in 'path'.
118 if (pos == absl::string_view::npos)
119 return std::make_pair(path.substr(0, 0), path);
120
121 // Handle the case with a single leading '/' in 'path'.
122 if (pos == 0)
123 return std::make_pair(path.substr(0, 1), absl::ClippedSubstr(path, 1));
124
125 return std::make_pair(path.substr(0, pos),
126 absl::ClippedSubstr(path, pos + 1));
127}
128
129absl::string_view Stem(absl::string_view path) {
130 return internal::SplitBasename(path).first;
131}
132
133absl::string_view Extension(absl::string_view path) {
134 return internal::SplitBasename(path).second;
135}
136
137std::string CleanPath(const absl::string_view unclean_path) {
138 std::string path = std::string(unclean_path);
139 const char* src = path.c_str();
140 std::string::iterator dst = path.begin();
141
142 // Check for absolute path and determine initial backtrack limit.
143 const bool is_absolute_path = *src == '/';
144 if (is_absolute_path) {
145 *dst++ = *src++;
146 while (*src == '/') ++src;
147 }
148 std::string::const_iterator backtrack_limit = dst;
149
150 // Process all parts
151 while (*src) {
152 bool parsed = false;
153
154 if (src[0] == '.') {
155 // 1dot ".<whateverisnext>", check for END or SEP.
156 if (src[1] == '/' || !src[1]) {
157 if (*++src) {
158 ++src;
159 }
160 parsed = true;
161 } else if (src[1] == '.' && (src[2] == '/' || !src[2])) {
162 // 2dot END or SEP (".." | "../<whateverisnext>").
163 src += 2;
164 if (dst != backtrack_limit) {
165 // We can backtrack the previous part
166 for (--dst; dst != backtrack_limit && dst[-1] != '/'; --dst) {
167 // Empty.
168 }
169 } else if (!is_absolute_path) {
170 // Failed to backtrack and we can't skip it either. Rewind and copy.
171 src -= 2;
172 *dst++ = *src++;
173 *dst++ = *src++;
174 if (*src) {
175 *dst++ = *src;
176 }
177 // We can never backtrack over a copied "../" part so set new limit.
178 backtrack_limit = dst;
179 }
180 if (*src) {
181 ++src;
182 }
183 parsed = true;
184 }
185 }
186
187 // If not parsed, copy entire part until the next SEP or EOS.
188 if (!parsed) {
189 while (*src && *src != '/') {
190 *dst++ = *src++;
191 }
192 if (*src) {
193 *dst++ = *src++;
194 }
195 }
196
197 // Skip consecutive SEP occurrences
198 while (*src == '/') {
199 ++src;
200 }
201 }
202
203 // Calculate and check the length of the cleaned path.
204 int path_length = dst - path.begin();
205 if (path_length != 0) {
206 // Remove trailing '/' except if it is root path ("/" ==> path_length := 1)
207 if (path_length > 1 && path[path_length - 1] == '/') {
208 --path_length;
209 }
210 path.resize(path_length);
211 } else {
212 // The cleaned path is empty; assign "." as per the spec.
213 path.assign(1, '.');
214 }
215 return path;
216}
217
218std::string CollapseSlashes(absl::string_view path) {
219 std::string ret;
220 ret.reserve(path.size());
221 bool prev_was_slash = false;
222 for (char c : path) {
223 if (c == '/') {
224 if (prev_was_slash) {
225 continue;
226 }
227 prev_was_slash = true;
228 } else {
229 prev_was_slash = false;
230 }
231 ret.push_back(c);
232 }
233 return ret;
234}
235
236} // namespace file
std::pair< absl::string_view, absl::string_view > SplitBasename(absl::string_view path)
Definition path.cc:79
std::string JoinPathImpl(bool honor_abs, std::initializer_list< absl::string_view > paths)
Not part of the public API.
Definition path.cc:41
Definition file.cc:169
std::string AddSlash(absl::string_view path)
Definition path.cc:96
absl::string_view Extension(absl::string_view path)
Definition path.cc:133
std::string CleanPath(const absl::string_view unclean_path)
Definition path.cc:137
std::pair< absl::string_view, absl::string_view > SplitPath(absl::string_view path)
Definition path.cc:113
bool IsAbsolutePath(absl::string_view path)
Return true if path is absolute.
Definition path.cc:92
absl::string_view Stem(absl::string_view path)
Definition path.cc:129
std::string JoinPath()
Definition path.h:82
absl::string_view Basename(absl::string_view path)
Definition path.cc:109
absl::string_view Dirname(absl::string_view path)
Definition path.cc:105
std::string CollapseSlashes(absl::string_view path)
Definition path.cc:218