]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/quickbook/src/include_paths.cpp
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / tools / quickbook / src / include_paths.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 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 "native_text.hpp"
13 #include "glob.hpp"
14 #include "include_paths.hpp"
15 #include "state.hpp"
16 #include "utils.hpp"
17 #include "quickbook.hpp" // For the include_path global (yuck)
18 #include <boost/foreach.hpp>
19 #include <boost/range/algorithm/replace.hpp>
20 #include <boost/filesystem/operations.hpp>
21 #include <cassert>
22
23 namespace quickbook
24 {
25 //
26 // check_path
27 //
28
29 path_parameter check_path(value const& path, quickbook::state& state)
30 {
31 if (qbk_version_n >= 107u) {
32 std::string path_text = path.get_encoded();
33
34 try {
35 if (check_glob(path_text)) {
36 return path_parameter(path_text, path_parameter::glob);
37 }
38 else {
39 return path_parameter(glob_unescape(path_text),
40 path_parameter::path);
41 }
42 } catch(glob_error& e) {
43 detail::outerr(path.get_file(), path.get_position())
44 << "Invalid path (" << e.what() << "): "
45 << path_text
46 << std::endl;
47 ++state.error_count;
48 return path_parameter(path_text, path_parameter::invalid);
49 }
50 }
51 else {
52 // Paths are encoded for quickbook 1.6+ and also xmlbase
53 // values (technically xmlbase is a 1.6 feature, but that
54 // isn't enforced as it's backwards compatible).
55 //
56 // Counter-intuitively: encoded == plain text here.
57
58 std::string path_text = qbk_version_n >= 106u || path.is_encoded() ?
59 path.get_encoded() : detail::to_s(path.get_quickbook());
60
61 if (path_text.find('\\') != std::string::npos)
62 {
63 quickbook::detail::ostream* err;
64
65 if (qbk_version_n >= 106u) {
66 err = &detail::outerr(path.get_file(), path.get_position());
67 ++state.error_count;
68 }
69 else {
70 err = &detail::outwarn(path.get_file(), path.get_position());
71 }
72
73 *err << "Path isn't portable: '"
74 << path_text
75 << "'"
76 << std::endl;
77
78 boost::replace(path_text, '\\', '/');
79 }
80
81 return path_parameter(path_text, path_parameter::path);
82 }
83 }
84
85 path_parameter check_xinclude_path(value const& p, quickbook::state& state)
86 {
87 path_parameter parameter = check_path(p, state);
88
89 if (parameter.type == path_parameter::glob) {
90 detail::outerr(p.get_file(), p.get_position())
91 << "Glob used for xml path."
92 << std::endl;
93 ++state.error_count;
94 parameter.type = path_parameter::invalid;
95 }
96
97 return parameter;
98 }
99
100 //
101 // Search include path
102 //
103
104 void include_search_glob(std::set<quickbook_path> & result,
105 quickbook_path const& location,
106 std::string path, quickbook::state& state)
107 {
108 std::size_t glob_pos = find_glob_char(path);
109
110 if (glob_pos == std::string::npos)
111 {
112 quickbook_path complete_path = location / glob_unescape(path);
113
114 if (fs::exists(complete_path.file_path))
115 {
116 state.dependencies.add_glob_match(complete_path.file_path);
117 result.insert(complete_path);
118 }
119 return;
120 }
121
122 std::size_t prev = path.rfind('/', glob_pos);
123 std::size_t next = path.find('/', glob_pos);
124
125 std::size_t glob_begin = prev == std::string::npos ? 0 : prev + 1;
126 std::size_t glob_end = next == std::string::npos ? path.size() : next;
127
128 quickbook_path new_location = location;
129
130 if (prev != std::string::npos) {
131 new_location /= glob_unescape(path.substr(0, prev));
132 }
133
134 if (next != std::string::npos) ++next;
135
136 boost::string_ref glob(
137 path.data() + glob_begin,
138 glob_end - glob_begin);
139
140 fs::path base_dir = new_location.file_path.empty() ?
141 fs::path(".") : new_location.file_path;
142 if (!fs::is_directory(base_dir)) return;
143
144 // Walk through the dir for matches.
145 for (fs::directory_iterator dir_i(base_dir), dir_e;
146 dir_i != dir_e; ++dir_i)
147 {
148 fs::path f = dir_i->path().filename();
149 std::string generic_path = detail::path_to_generic(f);
150
151 // Skip if the dir item doesn't match.
152 if (!quickbook::glob(glob, generic_path)) continue;
153
154 // If it's a file we add it to the results.
155 if (next == std::string::npos)
156 {
157 if (fs::is_regular_file(dir_i->status()))
158 {
159 quickbook_path r = new_location / generic_path;
160 state.dependencies.add_glob_match(r.file_path);
161 result.insert(r);
162 }
163 }
164 // If it's a matching dir, we recurse looking for more files.
165 else
166 {
167 if (!fs::is_regular_file(dir_i->status()))
168 {
169 include_search_glob(result, new_location / generic_path,
170 path.substr(next), state);
171 }
172 }
173 }
174 }
175
176 std::set<quickbook_path> include_search(path_parameter const& parameter,
177 quickbook::state& state, string_iterator pos)
178 {
179 std::set<quickbook_path> result;
180
181 switch (parameter.type) {
182 case path_parameter::glob:
183 // If the path has some glob match characters
184 // we do a discovery of all the matches..
185 {
186 fs::path current = state.current_file->path.parent_path();
187
188 // Search for the current dir accumulating to the result.
189 state.dependencies.add_glob(current / parameter.value);
190 include_search_glob(result, state.current_path.parent_path(),
191 parameter.value, state);
192
193 // Search the include path dirs accumulating to the result.
194 unsigned count = 0;
195 BOOST_FOREACH(fs::path dir, include_path)
196 {
197 ++count;
198 state.dependencies.add_glob(dir / parameter.value);
199 include_search_glob(result,
200 quickbook_path(dir, count, fs::path()),
201 parameter.value, state);
202 }
203
204 // Done.
205 return result;
206 }
207
208 case path_parameter::path:
209 {
210 fs::path path = detail::generic_to_path(parameter.value);
211
212 // If the path is relative, try and resolve it.
213 if (!path.has_root_directory() && !path.has_root_name())
214 {
215 quickbook_path path2 =
216 state.current_path.parent_path() / parameter.value;
217
218 // See if it can be found locally first.
219 if (state.dependencies.add_dependency(path2.file_path))
220 {
221 result.insert(path2);
222 return result;
223 }
224
225 // Search in each of the include path locations.
226 unsigned count = 0;
227 BOOST_FOREACH(fs::path full, include_path)
228 {
229 ++count;
230 full /= path;
231
232 if (state.dependencies.add_dependency(full))
233 {
234 result.insert(quickbook_path(full, count, path));
235 return result;
236 }
237 }
238 }
239 else
240 {
241 if (state.dependencies.add_dependency(path)) {
242 result.insert(quickbook_path(path, 0, path));
243 return result;
244 }
245 }
246
247 detail::outerr(state.current_file, pos)
248 << "Unable to find file: "
249 << parameter.value
250 << std::endl;
251 ++state.error_count;
252
253 return result;
254 }
255
256 case path_parameter::invalid:
257 return result;
258
259 default:
260 assert(0);
261 return result;
262 }
263 }
264
265 //
266 // quickbook_path
267 //
268
269 void swap(quickbook_path& x, quickbook_path& y) {
270 boost::swap(x.file_path, y.file_path);
271 boost::swap(x.include_path_offset, y.include_path_offset);
272 boost::swap(x.abstract_file_path, y.abstract_file_path);
273 }
274
275 bool quickbook_path::operator<(quickbook_path const& other) const
276 {
277 // TODO: Is comparing file_path redundant? Surely if quickbook_path
278 // and abstract_file_path are equal, it must also be.
279 // (but not vice-versa)
280 return
281 abstract_file_path != other.abstract_file_path ?
282 abstract_file_path < other.abstract_file_path :
283 include_path_offset != other.include_path_offset ?
284 include_path_offset < other.include_path_offset :
285 file_path < other.file_path;
286 }
287
288 quickbook_path quickbook_path::operator/(boost::string_ref x) const
289 {
290 return quickbook_path(*this) /= x;
291 }
292
293 quickbook_path& quickbook_path::operator/=(boost::string_ref x)
294 {
295 fs::path x2 = detail::generic_to_path(x);
296 file_path /= x2;
297 abstract_file_path /= x2;
298 return *this;
299 }
300
301 quickbook_path quickbook_path::parent_path() const
302 {
303 return quickbook_path(file_path.parent_path(), include_path_offset,
304 abstract_file_path.parent_path());
305 }
306
307 // Not a general purpose normalization function, just
308 // from paths from the root directory. It strips the excess
309 // ".." parts from a path like: "x/../../y", leaving "y".
310 std::vector<fs::path> normalize_path_from_root(fs::path const& path)
311 {
312 assert(!path.has_root_directory() && !path.has_root_name());
313
314 std::vector<fs::path> parts;
315
316 BOOST_FOREACH(fs::path const& part, path)
317 {
318 if (part.empty() || part == ".") {
319 }
320 else if (part == "..") {
321 if (!parts.empty()) parts.pop_back();
322 }
323 else {
324 parts.push_back(part);
325 }
326 }
327
328 return parts;
329 }
330
331 // The relative path from base to path
332 fs::path path_difference(fs::path const& base, fs::path const& path)
333 {
334 fs::path
335 absolute_base = fs::absolute(base),
336 absolute_path = fs::absolute(path);
337
338 // Remove '.', '..' and empty parts from the remaining path
339 std::vector<fs::path>
340 base_parts = normalize_path_from_root(absolute_base.relative_path()),
341 path_parts = normalize_path_from_root(absolute_path.relative_path());
342
343 std::vector<fs::path>::iterator
344 base_it = base_parts.begin(),
345 base_end = base_parts.end(),
346 path_it = path_parts.begin(),
347 path_end = path_parts.end();
348
349 // Build up the two paths in these variables, checking for the first
350 // difference.
351 fs::path
352 base_tmp = absolute_base.root_path(),
353 path_tmp = absolute_path.root_path();
354
355 fs::path result;
356
357 // If they have different roots then there's no relative path so
358 // just build an absolute path.
359 if (!fs::equivalent(base_tmp, path_tmp))
360 {
361 result = path_tmp;
362 }
363 else
364 {
365 // Find the point at which the paths differ
366 for(; base_it != base_end && path_it != path_end; ++base_it, ++path_it)
367 {
368 if(!fs::equivalent(base_tmp /= *base_it, path_tmp /= *path_it))
369 break;
370 }
371
372 // Build a relative path to that point
373 for(; base_it != base_end; ++base_it) result /= "..";
374 }
375
376 // Build the rest of our path
377 for(; path_it != path_end; ++path_it) result /= *path_it;
378
379 return result;
380 }
381
382 // Convert a Boost.Filesystem path to a URL.
383 //
384 // I'm really not sure about this, as the meaning of root_name and
385 // root_directory are only clear for windows.
386 //
387 // Some info on file URLs at:
388 // https://en.wikipedia.org/wiki/File_URI_scheme
389 std::string file_path_to_url(fs::path const& x)
390 {
391 // TODO: Maybe some kind of error if this doesn't understand the path.
392 // TODO: Might need a special cygwin implementation.
393 // TODO: What if x.has_root_name() && !x.has_root_directory()?
394 // TODO: What does Boost.Filesystem do for '//localhost/c:/path'?
395 // Is that event allowed by windows?
396
397 if (x.has_root_name()) {
398 std::string root_name = detail::path_to_generic(x.root_name());
399
400 if (root_name.size() > 2 && root_name[0] == '/' && root_name[1] == '/') {
401 // root_name is a network location.
402 return "file:" + detail::escape_uri(detail::path_to_generic(x));
403 }
404 else if (root_name.size() >= 2 && root_name[root_name.size() - 1] == ':') {
405 // root_name is a drive.
406 return "file:///"
407 + detail::escape_uri(root_name.substr(0, root_name.size() - 1))
408 + ":/" // TODO: Or maybe "|/".
409 + detail::escape_uri(detail::path_to_generic(x.relative_path()));
410 }
411 else {
412 // Not sure what root_name is.
413 return detail::escape_uri(detail::path_to_generic(x));
414 }
415 }
416 else if (x.has_root_directory()) {
417 return "file://" + detail::escape_uri(detail::path_to_generic(x));
418 }
419 else {
420 return detail::escape_uri(detail::path_to_generic(x));
421 }
422 }
423
424 std::string dir_path_to_url(fs::path const& x)
425 {
426 return file_path_to_url(x) + "/";
427 }
428
429 quickbook_path resolve_xinclude_path(std::string const& x, quickbook::state& state) {
430 fs::path path = detail::generic_to_path(x);
431 fs::path full_path = path;
432
433 // If the path is relative
434 if (!path.has_root_directory())
435 {
436 // Resolve the path from the current file
437 full_path = state.current_file->path.parent_path() / path;
438
439 // Then calculate relative to the current xinclude_base.
440 path = path_difference(state.xinclude_base, full_path);
441 }
442
443 return quickbook_path(full_path, 0, path);
444 }
445 }