]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
b32b8144 | 12 | #include "stream.hpp" |
7c673cae FG |
13 | #include "glob.hpp" |
14 | #include "include_paths.hpp" | |
b32b8144 | 15 | #include "path.hpp" |
7c673cae FG |
16 | #include "state.hpp" |
17 | #include "utils.hpp" | |
18 | #include "quickbook.hpp" // For the include_path global (yuck) | |
19 | #include <boost/foreach.hpp> | |
20 | #include <boost/range/algorithm/replace.hpp> | |
21 | #include <boost/filesystem/operations.hpp> | |
22 | #include <cassert> | |
23 | ||
24 | namespace quickbook | |
25 | { | |
26 | // | |
27 | // check_path | |
28 | // | |
29 | ||
30 | path_parameter check_path(value const& path, quickbook::state& state) | |
31 | { | |
32 | if (qbk_version_n >= 107u) { | |
33 | std::string path_text = path.get_encoded(); | |
b32b8144 FG |
34 | if (path_text.empty()) |
35 | { | |
36 | detail::outerr(path.get_file(), path.get_position()) | |
37 | << "Empty path argument" | |
38 | << "std::endl"; | |
39 | ++state.error_count; | |
40 | return path_parameter(path_text, path_parameter::invalid); | |
41 | } | |
7c673cae FG |
42 | |
43 | try { | |
44 | if (check_glob(path_text)) { | |
45 | return path_parameter(path_text, path_parameter::glob); | |
46 | } | |
47 | else { | |
48 | return path_parameter(glob_unescape(path_text), | |
49 | path_parameter::path); | |
50 | } | |
51 | } catch(glob_error& e) { | |
52 | detail::outerr(path.get_file(), path.get_position()) | |
53 | << "Invalid path (" << e.what() << "): " | |
54 | << path_text | |
55 | << std::endl; | |
56 | ++state.error_count; | |
57 | return path_parameter(path_text, path_parameter::invalid); | |
58 | } | |
59 | } | |
60 | else { | |
61 | // Paths are encoded for quickbook 1.6+ and also xmlbase | |
62 | // values (technically xmlbase is a 1.6 feature, but that | |
63 | // isn't enforced as it's backwards compatible). | |
64 | // | |
65 | // Counter-intuitively: encoded == plain text here. | |
66 | ||
67 | std::string path_text = qbk_version_n >= 106u || path.is_encoded() ? | |
b32b8144 | 68 | path.get_encoded() : path.get_quickbook().to_s(); |
7c673cae | 69 | |
b32b8144 FG |
70 | if (path_text.empty()) |
71 | { | |
72 | detail::outerr(path.get_file(), path.get_position()) | |
73 | << "Empty path argument" | |
74 | << std::endl; | |
75 | ++state.error_count; | |
76 | return path_parameter(path_text, path_parameter::invalid); | |
77 | } | |
78 | ||
79 | // Check for windows paths, an error in quickbook 1.6 | |
80 | // In quickbook 1.7 backslash is used as an escape character | |
81 | // for glob characters. | |
7c673cae FG |
82 | if (path_text.find('\\') != std::string::npos) |
83 | { | |
84 | quickbook::detail::ostream* err; | |
85 | ||
86 | if (qbk_version_n >= 106u) { | |
87 | err = &detail::outerr(path.get_file(), path.get_position()); | |
88 | ++state.error_count; | |
89 | } | |
90 | else { | |
91 | err = &detail::outwarn(path.get_file(), path.get_position()); | |
92 | } | |
93 | ||
94 | *err << "Path isn't portable: '" | |
95 | << path_text | |
96 | << "'" | |
97 | << std::endl; | |
98 | ||
99 | boost::replace(path_text, '\\', '/'); | |
100 | } | |
101 | ||
102 | return path_parameter(path_text, path_parameter::path); | |
103 | } | |
104 | } | |
105 | ||
106 | path_parameter check_xinclude_path(value const& p, quickbook::state& state) | |
107 | { | |
108 | path_parameter parameter = check_path(p, state); | |
109 | ||
110 | if (parameter.type == path_parameter::glob) { | |
111 | detail::outerr(p.get_file(), p.get_position()) | |
112 | << "Glob used for xml path." | |
113 | << std::endl; | |
114 | ++state.error_count; | |
115 | parameter.type = path_parameter::invalid; | |
116 | } | |
117 | ||
118 | return parameter; | |
119 | } | |
120 | ||
121 | // | |
122 | // Search include path | |
123 | // | |
124 | ||
125 | void include_search_glob(std::set<quickbook_path> & result, | |
126 | quickbook_path const& location, | |
127 | std::string path, quickbook::state& state) | |
128 | { | |
129 | std::size_t glob_pos = find_glob_char(path); | |
130 | ||
131 | if (glob_pos == std::string::npos) | |
132 | { | |
133 | quickbook_path complete_path = location / glob_unescape(path); | |
134 | ||
135 | if (fs::exists(complete_path.file_path)) | |
136 | { | |
137 | state.dependencies.add_glob_match(complete_path.file_path); | |
138 | result.insert(complete_path); | |
139 | } | |
140 | return; | |
141 | } | |
142 | ||
143 | std::size_t prev = path.rfind('/', glob_pos); | |
144 | std::size_t next = path.find('/', glob_pos); | |
145 | ||
146 | std::size_t glob_begin = prev == std::string::npos ? 0 : prev + 1; | |
147 | std::size_t glob_end = next == std::string::npos ? path.size() : next; | |
148 | ||
149 | quickbook_path new_location = location; | |
150 | ||
151 | if (prev != std::string::npos) { | |
152 | new_location /= glob_unescape(path.substr(0, prev)); | |
153 | } | |
154 | ||
155 | if (next != std::string::npos) ++next; | |
156 | ||
b32b8144 | 157 | quickbook::string_view glob( |
7c673cae FG |
158 | path.data() + glob_begin, |
159 | glob_end - glob_begin); | |
160 | ||
161 | fs::path base_dir = new_location.file_path.empty() ? | |
162 | fs::path(".") : new_location.file_path; | |
163 | if (!fs::is_directory(base_dir)) return; | |
164 | ||
165 | // Walk through the dir for matches. | |
166 | for (fs::directory_iterator dir_i(base_dir), dir_e; | |
167 | dir_i != dir_e; ++dir_i) | |
168 | { | |
169 | fs::path f = dir_i->path().filename(); | |
170 | std::string generic_path = detail::path_to_generic(f); | |
171 | ||
172 | // Skip if the dir item doesn't match. | |
173 | if (!quickbook::glob(glob, generic_path)) continue; | |
174 | ||
175 | // If it's a file we add it to the results. | |
176 | if (next == std::string::npos) | |
177 | { | |
178 | if (fs::is_regular_file(dir_i->status())) | |
179 | { | |
180 | quickbook_path r = new_location / generic_path; | |
181 | state.dependencies.add_glob_match(r.file_path); | |
182 | result.insert(r); | |
183 | } | |
184 | } | |
185 | // If it's a matching dir, we recurse looking for more files. | |
186 | else | |
187 | { | |
188 | if (!fs::is_regular_file(dir_i->status())) | |
189 | { | |
190 | include_search_glob(result, new_location / generic_path, | |
191 | path.substr(next), state); | |
192 | } | |
193 | } | |
194 | } | |
195 | } | |
196 | ||
197 | std::set<quickbook_path> include_search(path_parameter const& parameter, | |
198 | quickbook::state& state, string_iterator pos) | |
199 | { | |
200 | std::set<quickbook_path> result; | |
201 | ||
202 | switch (parameter.type) { | |
203 | case path_parameter::glob: | |
204 | // If the path has some glob match characters | |
205 | // we do a discovery of all the matches.. | |
206 | { | |
207 | fs::path current = state.current_file->path.parent_path(); | |
208 | ||
209 | // Search for the current dir accumulating to the result. | |
210 | state.dependencies.add_glob(current / parameter.value); | |
211 | include_search_glob(result, state.current_path.parent_path(), | |
212 | parameter.value, state); | |
213 | ||
214 | // Search the include path dirs accumulating to the result. | |
215 | unsigned count = 0; | |
216 | BOOST_FOREACH(fs::path dir, include_path) | |
217 | { | |
218 | ++count; | |
219 | state.dependencies.add_glob(dir / parameter.value); | |
220 | include_search_glob(result, | |
221 | quickbook_path(dir, count, fs::path()), | |
222 | parameter.value, state); | |
223 | } | |
224 | ||
225 | // Done. | |
226 | return result; | |
227 | } | |
228 | ||
229 | case path_parameter::path: | |
230 | { | |
231 | fs::path path = detail::generic_to_path(parameter.value); | |
232 | ||
233 | // If the path is relative, try and resolve it. | |
234 | if (!path.has_root_directory() && !path.has_root_name()) | |
235 | { | |
236 | quickbook_path path2 = | |
237 | state.current_path.parent_path() / parameter.value; | |
238 | ||
239 | // See if it can be found locally first. | |
240 | if (state.dependencies.add_dependency(path2.file_path)) | |
241 | { | |
242 | result.insert(path2); | |
243 | return result; | |
244 | } | |
245 | ||
246 | // Search in each of the include path locations. | |
247 | unsigned count = 0; | |
248 | BOOST_FOREACH(fs::path full, include_path) | |
249 | { | |
250 | ++count; | |
251 | full /= path; | |
252 | ||
253 | if (state.dependencies.add_dependency(full)) | |
254 | { | |
255 | result.insert(quickbook_path(full, count, path)); | |
256 | return result; | |
257 | } | |
258 | } | |
259 | } | |
260 | else | |
261 | { | |
262 | if (state.dependencies.add_dependency(path)) { | |
263 | result.insert(quickbook_path(path, 0, path)); | |
264 | return result; | |
265 | } | |
266 | } | |
267 | ||
268 | detail::outerr(state.current_file, pos) | |
269 | << "Unable to find file: " | |
270 | << parameter.value | |
271 | << std::endl; | |
272 | ++state.error_count; | |
273 | ||
274 | return result; | |
275 | } | |
276 | ||
277 | case path_parameter::invalid: | |
278 | return result; | |
279 | ||
280 | default: | |
281 | assert(0); | |
282 | return result; | |
283 | } | |
284 | } | |
285 | ||
286 | // | |
287 | // quickbook_path | |
288 | // | |
289 | ||
290 | void swap(quickbook_path& x, quickbook_path& y) { | |
291 | boost::swap(x.file_path, y.file_path); | |
292 | boost::swap(x.include_path_offset, y.include_path_offset); | |
293 | boost::swap(x.abstract_file_path, y.abstract_file_path); | |
294 | } | |
295 | ||
296 | bool quickbook_path::operator<(quickbook_path const& other) const | |
297 | { | |
7c673cae FG |
298 | return |
299 | abstract_file_path != other.abstract_file_path ? | |
300 | abstract_file_path < other.abstract_file_path : | |
301 | include_path_offset != other.include_path_offset ? | |
302 | include_path_offset < other.include_path_offset : | |
303 | file_path < other.file_path; | |
304 | } | |
305 | ||
b32b8144 | 306 | quickbook_path quickbook_path::operator/(quickbook::string_view x) const |
7c673cae FG |
307 | { |
308 | return quickbook_path(*this) /= x; | |
309 | } | |
310 | ||
b32b8144 | 311 | quickbook_path& quickbook_path::operator/=(quickbook::string_view x) |
7c673cae FG |
312 | { |
313 | fs::path x2 = detail::generic_to_path(x); | |
314 | file_path /= x2; | |
315 | abstract_file_path /= x2; | |
316 | return *this; | |
317 | } | |
318 | ||
319 | quickbook_path quickbook_path::parent_path() const | |
320 | { | |
321 | return quickbook_path(file_path.parent_path(), include_path_offset, | |
322 | abstract_file_path.parent_path()); | |
323 | } | |
324 | ||
b32b8144 | 325 | quickbook_path resolve_xinclude_path(std::string const& x, quickbook::state& state, bool is_file) { |
7c673cae FG |
326 | fs::path path = detail::generic_to_path(x); |
327 | fs::path full_path = path; | |
328 | ||
329 | // If the path is relative | |
330 | if (!path.has_root_directory()) | |
331 | { | |
332 | // Resolve the path from the current file | |
333 | full_path = state.current_file->path.parent_path() / path; | |
334 | ||
335 | // Then calculate relative to the current xinclude_base. | |
b32b8144 | 336 | path = path_difference(state.xinclude_base, full_path, is_file); |
7c673cae FG |
337 | } |
338 | ||
339 | return quickbook_path(full_path, 0, path); | |
340 | } | |
341 | } |