]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/quickbook/src/path.cpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / tools / quickbook / src / path.cpp
1 /*=============================================================================
2 Copyright (c) 2002 2004 2006 Joel de Guzman
3 Copyright (c) 2004 Eric Niebler
4 Copyright (c) 2005 Thomas Guest
5 Copyright (c) 2013, 2017 Daniel James
6
7 Use, modification and distribution is subject to the Boost Software
8 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
9 http://www.boost.org/LICENSE_1_0.txt)
10 =============================================================================*/
11
12 #include "path.hpp"
13 #include <cassert>
14 #include <boost/filesystem/operations.hpp>
15 #include <boost/range/algorithm/replace.hpp>
16 #include "for.hpp"
17 #include "glob.hpp"
18 #include "include_paths.hpp"
19 #include "state.hpp"
20 #include "utils.hpp"
21
22 #if QUICKBOOK_CYGWIN_PATHS
23 #include <sys/cygwin.h>
24 #include <boost/scoped_array.hpp>
25 #endif
26
27 namespace quickbook
28 {
29 // Not a general purpose normalization function, just
30 // from paths from the root directory. It strips the excess
31 // ".." parts from a path like: "x/../../y", leaving "y".
32 std::vector<fs::path> remove_dots_from_path(fs::path const& path)
33 {
34 assert(!path.has_root_directory() && !path.has_root_name());
35
36 std::vector<fs::path> parts;
37
38 QUICKBOOK_FOR (fs::path const& part, path) {
39 if (part.empty() || part == ".") {
40 }
41 else if (part == "..") {
42 if (!parts.empty()) parts.pop_back();
43 }
44 else {
45 parts.push_back(part);
46 }
47 }
48
49 return parts;
50 }
51
52 // The relative path from base to path
53 fs::path path_difference(
54 fs::path const& base, fs::path const& path, bool is_file)
55 {
56 fs::path absolute_base = fs::absolute(base),
57 absolute_path = fs::absolute(path);
58
59 // Remove '.', '..' and empty parts from the remaining path
60 std::vector<fs::path> base_parts = remove_dots_from_path(
61 absolute_base.relative_path()),
62 path_parts = remove_dots_from_path(
63 absolute_path.relative_path());
64
65 std::vector<fs::path>::iterator base_it = base_parts.begin(),
66 base_end = base_parts.end(),
67 path_it = path_parts.begin(),
68 path_end = path_parts.end();
69
70 // Build up the two paths in these variables, checking for the first
71 // difference.
72 fs::path base_tmp = absolute_base.root_path(),
73 path_tmp = absolute_path.root_path();
74
75 fs::path result;
76
77 // If they have different roots then there's no relative path so
78 // just build an absolute path.
79 if (!fs::equivalent(base_tmp, path_tmp)) {
80 result = path_tmp;
81 }
82 else {
83 // Find the point at which the paths differ
84 for (; base_it != base_end && path_it != path_end;
85 ++base_it, ++path_it) {
86 base_tmp /= *base_it;
87 path_tmp /= *path_it;
88 if (*base_it != *path_it) {
89 if (!fs::exists(base_tmp) || !fs::exists(path_tmp) ||
90 !fs::equivalent(base_tmp, path_tmp)) {
91 break;
92 }
93 }
94 }
95
96 if (is_file && path_it == path_end &&
97 path_it != path_parts.begin()) {
98 --path_it;
99 result = "..";
100 }
101 else if (base_it == base_end && path_it == path_end) {
102 result = ".";
103 }
104
105 // Build a relative path to that point
106 for (; base_it != base_end; ++base_it)
107 result /= "..";
108 }
109
110 // Build the rest of our path
111 for (; path_it != path_end; ++path_it)
112 result /= *path_it;
113
114 return result;
115 }
116
117 // Convert a Boost.Filesystem path to a URL.
118 //
119 // I'm really not sure about this, as the meaning of root_name and
120 // root_directory are only clear for windows.
121 //
122 // Some info on file URLs at:
123 // https://en.wikipedia.org/wiki/File_URI_scheme
124 std::string file_path_to_url_impl(fs::path const& x, bool is_dir)
125 {
126 fs::path::const_iterator it = x.begin(), end = x.end();
127 if (it == end) {
128 return is_dir ? "./" : "";
129 }
130
131 std::string result;
132 bool sep = false;
133 std::string part;
134 if (x.has_root_name()) {
135 // Handle network address (e.g. \\example.com)
136 part = detail::path_to_generic(*it);
137 if (part.size() >= 2 && part[0] == '/' && part[1] == '/') {
138 result = "file:" + detail::escape_uri(part);
139 sep = true;
140 ++it;
141 if (it != end && *it == "/") {
142 result += "/";
143 sep = false;
144 ++it;
145 }
146 }
147 else {
148 result = "file:///";
149 }
150
151 // Handle windows root (e.g. c:)
152 if (it != end) {
153 part = detail::path_to_generic(*it);
154 if (part.size() >= 2 && part[part.size() - 1] == ':') {
155 result +=
156 detail::escape_uri(part.substr(0, part.size() - 1));
157 result += ':';
158 sep = false;
159 ++it;
160 }
161 }
162 }
163 else if (x.has_root_directory()) {
164 result = "file://";
165 sep = true;
166 }
167 else if (*it == ".") {
168 result = ".";
169 sep = true;
170 ++it;
171 }
172
173 for (; it != end; ++it) {
174 part = detail::path_to_generic(*it);
175 if (part == "/") {
176 result += "/";
177 sep = false;
178 }
179 else if (part == ".") {
180 // If the path has a trailing slash, write it out,
181 // even if is_dir is false.
182 if (sep) {
183 result += "/";
184 sep = false;
185 }
186 }
187 else {
188 if (sep) {
189 result += "/";
190 }
191 result += detail::escape_uri(detail::path_to_generic(*it));
192 sep = true;
193 }
194 }
195
196 if (is_dir && sep) {
197 result += "/";
198 }
199
200 return result;
201 }
202
203 std::string file_path_to_url(fs::path const& x)
204 {
205 return file_path_to_url_impl(x, false);
206 }
207
208 std::string dir_path_to_url(fs::path const& x)
209 {
210 return file_path_to_url_impl(x, true);
211 }
212
213 namespace detail
214 {
215 #if QUICKBOOK_WIDE_PATHS
216 std::string command_line_to_utf8(command_line_string const& x)
217 {
218 return to_utf8(x);
219 }
220 #else
221 std::string command_line_to_utf8(command_line_string const& x)
222 {
223 return x;
224 }
225 #endif
226
227 #if QUICKBOOK_WIDE_PATHS
228 fs::path generic_to_path(quickbook::string_view x)
229 {
230 return fs::path(from_utf8(x));
231 }
232
233 std::string path_to_generic(fs::path const& x)
234 {
235 return to_utf8(x.generic_wstring());
236 }
237 #else
238 fs::path generic_to_path(quickbook::string_view x)
239 {
240 return fs::path(x.begin(), x.end());
241 }
242
243 std::string path_to_generic(fs::path const& x)
244 {
245 return x.generic_string();
246 }
247 #endif
248
249 #if QUICKBOOK_CYGWIN_PATHS
250 fs::path command_line_to_path(command_line_string const& path)
251 {
252 cygwin_conv_path_t flags = CCP_POSIX_TO_WIN_W | CCP_RELATIVE;
253
254 ssize_t size = cygwin_conv_path(flags, path.c_str(), NULL, 0);
255
256 if (size < 0)
257 throw conversion_error(
258 "Error converting cygwin path to windows.");
259
260 boost::scoped_array<char> result(new char[size]);
261 void* ptr = result.get();
262
263 if (cygwin_conv_path(flags, path.c_str(), ptr, size))
264 throw conversion_error(
265 "Error converting cygwin path to windows.");
266
267 return fs::path(static_cast<wchar_t*>(ptr));
268 }
269
270 stream_string path_to_stream(fs::path const& path)
271 {
272 cygwin_conv_path_t flags = CCP_WIN_W_TO_POSIX | CCP_RELATIVE;
273
274 ssize_t size =
275 cygwin_conv_path(flags, path.native().c_str(), NULL, 0);
276
277 if (size < 0)
278 throw conversion_error(
279 "Error converting windows path to cygwin.");
280
281 boost::scoped_array<char> result(new char[size]);
282
283 if (cygwin_conv_path(
284 flags, path.native().c_str(), result.get(), size))
285 throw conversion_error(
286 "Error converting windows path to cygwin.");
287
288 return std::string(result.get());
289 }
290 #else
291 fs::path command_line_to_path(command_line_string const& path)
292 {
293 return fs::path(path);
294 }
295
296 #if QUICKBOOK_WIDE_PATHS && !QUICKBOOK_WIDE_STREAMS
297 stream_string path_to_stream(fs::path const& path)
298 {
299 return path.string();
300 }
301 #else
302 stream_string path_to_stream(fs::path const& path)
303 {
304 return path.native();
305 }
306 #endif
307
308 #endif // QUICKBOOK_CYGWIN_PATHS
309
310 enum path_or_url_type
311 {
312 path_or_url_empty = 0,
313 path_or_url_path,
314 path_or_url_url
315 };
316
317 path_or_url::path_or_url() : type_(path_or_url_empty) {}
318
319 path_or_url::path_or_url(path_or_url const& x)
320 : type_(x.type_), path_(x.path_), url_(x.url_)
321 {
322 }
323
324 path_or_url::path_or_url(command_line_string const& x)
325 {
326 auto rep = command_line_to_utf8(x);
327 auto it = rep.begin(), end = rep.end();
328 std::size_t count = 0;
329 while (it != end &&
330 ((*it >= 'a' && *it <= 'z') || (*it >= 'A' && *it <= 'Z') ||
331 *it == '+' || *it == '-' || *it == '.')) {
332 ++it;
333 ++count;
334 }
335
336 if (it != end && *it == ':' && count > 1) {
337 type_ = path_or_url_url;
338 }
339 else {
340 type_ = path_or_url_path;
341 }
342
343 switch (type_) {
344 case path_or_url_empty:
345 break;
346 case path_or_url_path:
347 path_ = command_line_to_path(x);
348 break;
349 case path_or_url_url:
350 url_ = rep;
351 break;
352 default:
353 assert(false);
354 }
355 }
356
357 path_or_url& path_or_url::operator=(path_or_url const& x)
358 {
359 type_ = x.type_;
360 path_ = x.path_;
361 url_ = x.url_;
362 return *this;
363 }
364
365 path_or_url& path_or_url::operator=(command_line_string const& x)
366 {
367 path_or_url tmp(x);
368 swap(tmp);
369 return *this;
370 }
371
372 void path_or_url::swap(path_or_url& x)
373 {
374 std::swap(type_, x.type_);
375 std::swap(path_, x.path_);
376 std::swap(url_, x.url_);
377 }
378
379 path_or_url path_or_url::url(string_view x)
380 {
381 path_or_url r;
382 r.type_ = path_or_url_url;
383 r.url_.assign(x.begin(), x.end());
384 return r;
385 }
386
387 path_or_url path_or_url::path(boost::filesystem::path const& x)
388 {
389 path_or_url r;
390 r.type_ = path_or_url_path;
391 r.path_ = x;
392 return r;
393 }
394
395 path_or_url::operator bool() const
396 {
397 return type_ != path_or_url_empty;
398 }
399
400 bool path_or_url::is_path() const { return type_ == path_or_url_path; }
401
402 bool path_or_url::is_url() const { return type_ == path_or_url_url; }
403
404 boost::filesystem::path const& path_or_url::get_path() const
405 {
406 assert(is_path());
407 return path_;
408 }
409
410 std::string const& path_or_url::get_url() const
411 {
412 assert(is_url());
413 return url_;
414 }
415
416 path_or_url path_or_url::operator/(string_view x) const
417 {
418 path_or_url r;
419 r.type_ = type_;
420
421 switch (type_) {
422 case path_or_url_empty:
423 assert(false);
424 break;
425 case path_or_url_path:
426 r.path_ = path_ / x.to_s();
427 break;
428 case path_or_url_url: {
429 r.url_ = url_;
430 auto pos = r.url_.rfind('/');
431 if (pos == std::string::npos) {
432 pos = r.url_.rfind(':');
433 }
434 if (pos != std::string::npos) {
435 r.url_.resize(pos + 1);
436 }
437 else {
438 // Error? Empty string?
439 r.url_ = "/";
440 }
441 r.url_ += x;
442 break;
443 }
444 default:
445 assert(false);
446 }
447 return r;
448 }
449 }
450 }