]>
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 | http://spirit.sourceforge.net/ | |
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 | #include <numeric> | |
12 | #include <functional> | |
13 | #include <vector> | |
14 | #include <map> | |
15 | #include <set> | |
16 | #include <boost/filesystem/convenience.hpp> | |
17 | #include <boost/filesystem/fstream.hpp> | |
18 | #include <boost/range/distance.hpp> | |
19 | #include <boost/range/algorithm/replace.hpp> | |
20 | #include <boost/lexical_cast.hpp> | |
21 | #include <boost/algorithm/string/replace.hpp> | |
22 | #include <boost/next_prior.hpp> | |
23 | #include <boost/foreach.hpp> | |
24 | #include "quickbook.hpp" | |
25 | #include "actions.hpp" | |
26 | #include "syntax_highlight.hpp" | |
27 | #include "utils.hpp" | |
28 | #include "files.hpp" | |
29 | #include "markups.hpp" | |
30 | #include "state.hpp" | |
31 | #include "state_save.hpp" | |
32 | #include "grammar.hpp" | |
b32b8144 | 33 | #include "stream.hpp" |
7c673cae FG |
34 | #include "block_tags.hpp" |
35 | #include "phrase_tags.hpp" | |
36 | #include "document_state.hpp" | |
b32b8144 | 37 | #include "path.hpp" |
7c673cae FG |
38 | |
39 | namespace quickbook | |
40 | { | |
41 | namespace { | |
42 | void write_anchors(quickbook::state& state, collector& tgt) | |
43 | { | |
7c673cae FG |
44 | if (state.source_mode_next) { |
45 | detail::outwarn(state.source_mode_next_pos.get_file(), | |
46 | state.source_mode_next_pos.get_position()) | |
47 | << "Temporary source mode unsupported here." | |
48 | << std::endl; | |
49 | state.source_mode_next = 0; | |
50 | } | |
51 | ||
52 | for(quickbook::state::string_list::iterator | |
53 | it = state.anchors.begin(), | |
54 | end = state.anchors.end(); | |
55 | it != end; ++it) | |
56 | { | |
57 | tgt << "<anchor id=\""; | |
58 | detail::print_string(*it, tgt.get()); | |
59 | tgt << "\"/>"; | |
60 | } | |
61 | ||
62 | state.anchors.clear(); | |
63 | } | |
64 | ||
65 | std::string add_anchor(quickbook::state& state, | |
b32b8144 | 66 | quickbook::string_view id, |
7c673cae FG |
67 | id_category::categories category = |
68 | id_category::explicit_anchor_id) | |
69 | { | |
70 | std::string placeholder = state.document.add_anchor(id, category); | |
71 | state.anchors.push_back(placeholder); | |
72 | return placeholder; | |
73 | } | |
74 | ||
75 | std::string get_attribute_value(quickbook::state& state, | |
76 | quickbook::value const& value) | |
77 | { | |
78 | std::string x = value.is_encoded() ? | |
b32b8144 | 79 | value.get_encoded() : value.get_quickbook().to_s(); |
7c673cae FG |
80 | |
81 | if (x.empty()) { | |
82 | detail::outerr(value.get_file(), value.get_position()) | |
83 | << "Empty attribute value." | |
84 | << std::endl; | |
85 | ++state.error_count; | |
86 | x = "xxx"; | |
87 | } | |
88 | ||
89 | return x; | |
90 | } | |
91 | ||
92 | std::string validate_id(quickbook::state& state, | |
93 | quickbook::value const& id_value) | |
94 | { | |
95 | bool valid = true; | |
96 | std::string id = get_attribute_value(state, id_value); | |
97 | ||
98 | // Special case since I use dollar ids for id placeholders. | |
99 | if (id[0] == '$') { valid = false; id[0] = '_'; } | |
100 | ||
101 | if (qbk_version_n >= 107u) { | |
102 | char const* allowed_punctuation = "_.-"; | |
103 | ||
104 | BOOST_FOREACH(char c, id) { | |
105 | if (!std::isalnum(c) && | |
106 | !std::strchr(allowed_punctuation, c)) | |
107 | valid = false; | |
108 | } | |
109 | } | |
110 | ||
111 | if (!valid) { | |
112 | detail::outerr(id_value.get_file(), id_value.get_position()) | |
113 | << "Invalid id: " | |
114 | << (id_value.is_encoded() ? id_value.get_encoded() : | |
b32b8144 | 115 | id_value.get_quickbook().to_s()) |
7c673cae FG |
116 | << std::endl; |
117 | ++state.error_count; | |
118 | } | |
119 | ||
120 | return id; | |
121 | } | |
122 | } | |
123 | ||
124 | bool quickbook_range::in_range() const { | |
125 | return qbk_version_n >= lower && qbk_version_n < upper; | |
126 | } | |
127 | ||
b32b8144 FG |
128 | bool quickbook_strict::is_strict_checking() const { |
129 | return state.strict_mode; | |
130 | } | |
131 | ||
132 | void list_action(quickbook::state&, value); | |
7c673cae FG |
133 | void header_action(quickbook::state&, value); |
134 | void begin_section_action(quickbook::state&, value); | |
135 | void end_section_action(quickbook::state&, value, string_iterator); | |
136 | void block_action(quickbook::state&, value); | |
137 | void block_empty_action(quickbook::state&, value); | |
138 | void macro_definition_action(quickbook::state&, value); | |
139 | void template_body_action(quickbook::state&, value); | |
140 | void variable_list_action(quickbook::state&, value); | |
141 | void table_action(quickbook::state&, value); | |
142 | void xinclude_action(quickbook::state&, value); | |
143 | void include_action(quickbook::state&, value, string_iterator); | |
144 | void image_action(quickbook::state&, value); | |
145 | void anchor_action(quickbook::state&, value); | |
146 | void link_action(quickbook::state&, value); | |
147 | void phrase_action(quickbook::state&, value); | |
148 | void role_action(quickbook::state&, value); | |
149 | void footnote_action(quickbook::state&, value); | |
150 | void raw_phrase_action(quickbook::state&, value); | |
151 | void source_mode_action(quickbook::state&, value); | |
152 | void next_source_mode_action(quickbook::state&, value); | |
153 | void code_action(quickbook::state&, value); | |
154 | void do_template_action(quickbook::state&, value, string_iterator); | |
155 | ||
156 | void element_action::operator()(parse_iterator first, parse_iterator) const | |
157 | { | |
158 | value_consumer values = state.values.release(); | |
159 | if(!values.check() || !state.conditional) return; | |
160 | value v = values.consume(); | |
161 | values.finish(); | |
162 | ||
163 | switch(v.get_tag()) | |
164 | { | |
165 | case block_tags::ordered_list: | |
166 | case block_tags::itemized_list: | |
b32b8144 | 167 | return list_action(state, v); |
7c673cae FG |
168 | case block_tags::generic_heading: |
169 | case block_tags::heading1: | |
170 | case block_tags::heading2: | |
171 | case block_tags::heading3: | |
172 | case block_tags::heading4: | |
173 | case block_tags::heading5: | |
174 | case block_tags::heading6: | |
175 | return header_action(state, v); | |
176 | case block_tags::begin_section: | |
177 | return begin_section_action(state, v); | |
178 | case block_tags::end_section: | |
179 | return end_section_action(state, v, first.base()); | |
180 | case block_tags::blurb: | |
181 | case block_tags::preformatted: | |
182 | case block_tags::blockquote: | |
183 | case block_tags::warning: | |
184 | case block_tags::caution: | |
185 | case block_tags::important: | |
186 | case block_tags::note: | |
187 | case block_tags::tip: | |
188 | case block_tags::block: | |
189 | return block_action(state,v); | |
190 | case block_tags::hr: | |
191 | return block_empty_action(state,v); | |
192 | case block_tags::macro_definition: | |
193 | return macro_definition_action(state,v); | |
194 | case block_tags::template_definition: | |
195 | return template_body_action(state,v); | |
196 | case block_tags::variable_list: | |
197 | return variable_list_action(state, v); | |
198 | case block_tags::table: | |
199 | return table_action(state, v); | |
200 | case block_tags::xinclude: | |
201 | return xinclude_action(state, v); | |
202 | case block_tags::import: | |
203 | case block_tags::include: | |
204 | return include_action(state, v, first.base()); | |
205 | case phrase_tags::image: | |
206 | return image_action(state, v); | |
207 | case phrase_tags::anchor: | |
208 | return anchor_action(state, v); | |
209 | case phrase_tags::url: | |
210 | case phrase_tags::link: | |
211 | case phrase_tags::funcref: | |
212 | case phrase_tags::classref: | |
213 | case phrase_tags::memberref: | |
214 | case phrase_tags::enumref: | |
215 | case phrase_tags::macroref: | |
216 | case phrase_tags::headerref: | |
217 | case phrase_tags::conceptref: | |
218 | case phrase_tags::globalref: | |
219 | return link_action(state, v); | |
220 | case phrase_tags::bold: | |
221 | case phrase_tags::italic: | |
222 | case phrase_tags::underline: | |
223 | case phrase_tags::teletype: | |
224 | case phrase_tags::strikethrough: | |
225 | case phrase_tags::quote: | |
226 | case phrase_tags::replaceable: | |
227 | return phrase_action(state, v); | |
228 | case phrase_tags::footnote: | |
229 | return footnote_action(state, v); | |
230 | case phrase_tags::escape: | |
231 | return raw_phrase_action(state, v); | |
232 | case phrase_tags::role: | |
233 | return role_action(state, v); | |
234 | case source_mode_tags::cpp: | |
235 | case source_mode_tags::python: | |
236 | case source_mode_tags::teletype: | |
237 | return source_mode_action(state, v); | |
238 | case code_tags::next_source_mode: | |
239 | return next_source_mode_action(state, v); | |
240 | case code_tags::code_block: | |
241 | case code_tags::inline_code_block: | |
242 | case code_tags::inline_code: | |
243 | return code_action(state, v); | |
244 | case template_tags::attribute_template: | |
245 | case template_tags::template_: | |
246 | return do_template_action(state, v, first.base()); | |
247 | default: | |
248 | break; | |
249 | } | |
250 | } | |
251 | ||
252 | void break_action::operator()(parse_iterator first, parse_iterator) const | |
253 | { | |
254 | write_anchors(state, state.phrase); | |
255 | ||
256 | if(*first == '\\') | |
257 | { | |
258 | detail::outwarn(state.current_file, first.base()) | |
259 | //<< "in column:" << pos.column << ", " | |
260 | << "'\\n' is deprecated, pleases use '[br]' instead" << ".\n"; | |
261 | } | |
262 | ||
263 | if(!state.warned_about_breaks) | |
264 | { | |
265 | detail::outwarn(state.current_file, first.base()) | |
266 | << "line breaks generate invalid boostbook " | |
267 | "(will only note first occurrence).\n"; | |
268 | ||
269 | state.warned_about_breaks = true; | |
270 | } | |
271 | ||
272 | state.phrase << detail::get_markup(phrase_tags::break_mark).pre; | |
273 | } | |
274 | ||
275 | void error_message_action::operator()(parse_iterator first, parse_iterator last) const | |
276 | { | |
277 | file_position const pos = state.current_file->position_of(first.base()); | |
278 | ||
279 | std::string value(first, last); | |
280 | std::string formatted_message = message; | |
281 | boost::replace_all(formatted_message, "%s", value); | |
282 | boost::replace_all(formatted_message, "%c", | |
283 | boost::lexical_cast<std::string>(pos.column)); | |
284 | ||
285 | detail::outerr(state.current_file->path, pos.line) | |
286 | << formatted_message << std::endl; | |
287 | ++state.error_count; | |
288 | } | |
289 | ||
290 | void error_action::operator()(parse_iterator first, parse_iterator /*last*/) const | |
291 | { | |
292 | file_position const pos = state.current_file->position_of(first.base()); | |
293 | ||
294 | detail::outerr(state.current_file->path, pos.line) | |
295 | << "Syntax Error near column " << pos.column << ".\n"; | |
296 | ++state.error_count; | |
297 | } | |
298 | ||
299 | void block_action(quickbook::state& state, value block) | |
300 | { | |
301 | write_anchors(state, state.out); | |
302 | ||
303 | detail::markup markup = detail::get_markup(block.get_tag()); | |
304 | ||
305 | value_consumer values = block; | |
306 | state.out << markup.pre << values.consume().get_encoded() << markup.post; | |
307 | values.finish(); | |
308 | } | |
309 | ||
310 | void block_empty_action(quickbook::state& state, value block) | |
311 | { | |
312 | write_anchors(state, state.out); | |
313 | ||
314 | detail::markup markup = detail::get_markup(block.get_tag()); | |
315 | state.out << markup.pre; | |
316 | } | |
317 | ||
318 | void phrase_action(quickbook::state& state, value phrase) | |
319 | { | |
320 | write_anchors(state, state.phrase); | |
321 | ||
322 | detail::markup markup = detail::get_markup(phrase.get_tag()); | |
323 | ||
324 | value_consumer values = phrase; | |
325 | state.phrase << markup.pre << values.consume().get_encoded() << markup.post; | |
326 | values.finish(); | |
327 | } | |
328 | ||
329 | void role_action(quickbook::state& state, value role_list) | |
330 | { | |
331 | write_anchors(state, state.phrase); | |
332 | ||
333 | value_consumer values = role_list; | |
334 | value role = values.consume(); | |
335 | value phrase = values.consume(); | |
336 | values.finish(); | |
337 | ||
338 | state.phrase | |
339 | << "<phrase role=\""; | |
340 | detail::print_string(get_attribute_value(state, role), | |
341 | state.phrase.get()); | |
342 | state.phrase | |
343 | << "\">" | |
344 | << phrase.get_encoded() | |
345 | << "</phrase>"; | |
346 | } | |
347 | ||
348 | void footnote_action(quickbook::state& state, value phrase) | |
349 | { | |
350 | write_anchors(state, state.phrase); | |
351 | ||
352 | value_consumer values = phrase; | |
353 | state.phrase | |
354 | << "<footnote id=\"" | |
355 | << state.document.add_id("f", id_category::numbered) | |
356 | << "\"><para>" | |
357 | << values.consume().get_encoded() | |
358 | << "</para></footnote>"; | |
359 | values.finish(); | |
360 | } | |
361 | ||
362 | void raw_phrase_action(quickbook::state& state, value phrase) | |
363 | { | |
364 | write_anchors(state, state.phrase); | |
365 | ||
366 | detail::markup markup = detail::get_markup(phrase.get_tag()); | |
367 | state.phrase << markup.pre << phrase.get_quickbook() << markup.post; | |
368 | } | |
369 | ||
370 | void paragraph_action::operator()() const | |
371 | { | |
372 | std::string str; | |
373 | state.phrase.swap(str); | |
374 | ||
375 | std::string::const_iterator | |
376 | pos = str.begin(), | |
377 | end = str.end(); | |
378 | ||
379 | while(pos != end && cl::space_p.test(*pos)) ++pos; | |
380 | ||
381 | if(pos != end) { | |
382 | detail::markup markup = state.in_list ? | |
383 | detail::get_markup(block_tags::paragraph_in_list) : | |
384 | detail::get_markup(block_tags::paragraph); | |
385 | state.out << markup.pre << str; | |
386 | write_anchors(state, state.out); | |
387 | state.out << markup.post; | |
388 | } | |
389 | } | |
390 | ||
391 | void explicit_list_action::operator()() const | |
392 | { | |
393 | state.explicit_list = true; | |
394 | } | |
395 | ||
396 | void phrase_end_action::operator()() const | |
397 | { | |
398 | write_anchors(state, state.phrase); | |
399 | } | |
400 | ||
401 | namespace { | |
402 | void write_bridgehead(quickbook::state& state, int level, | |
403 | std::string const& str, std::string const& id, bool self_link) | |
404 | { | |
405 | if (self_link && !id.empty()) | |
406 | { | |
407 | state.out << "<bridgehead renderas=\"sect" << level << "\""; | |
408 | state.out << " id=\""; | |
409 | state.out << state.document.add_id("h", id_category::numbered); | |
410 | state.out << "\">"; | |
411 | state.out << "<phrase id=\"" << id << "\"/>"; | |
412 | state.out << "<link linkend=\"" << id << "\">"; | |
413 | state.out << str; | |
414 | state.out << "</link>"; | |
415 | state.out << "</bridgehead>"; | |
416 | } | |
417 | else | |
418 | { | |
419 | state.out << "<bridgehead renderas=\"sect" << level << "\""; | |
420 | if(!id.empty()) state.out << " id=\"" << id << "\""; | |
421 | state.out << ">"; | |
422 | state.out << str; | |
423 | state.out << "</bridgehead>"; | |
424 | } | |
425 | } | |
426 | } | |
427 | ||
428 | void header_action(quickbook::state& state, value heading_list) | |
429 | { | |
430 | value_consumer values = heading_list; | |
431 | ||
432 | bool generic = heading_list.get_tag() == block_tags::generic_heading; | |
433 | value element_id = values.optional_consume(general_tags::element_id); | |
434 | value content = values.consume(); | |
435 | values.finish(); | |
436 | ||
437 | int level; | |
438 | ||
439 | if (generic) | |
440 | { | |
441 | level = state.document.section_level() + 1; | |
442 | // We need to use a heading which is one greater | |
443 | // than the current. | |
444 | if (level > 6 ) // The max is h6, clip it if it goes | |
445 | level = 6; // further than that | |
446 | } | |
447 | else | |
448 | { | |
449 | level = heading_list.get_tag() - block_tags::heading1 + 1; | |
450 | } | |
451 | ||
452 | write_anchors(state, state.out); | |
453 | ||
454 | if (!element_id.empty()) | |
455 | { | |
456 | // Use an explicit id. | |
457 | ||
458 | std::string anchor = state.document.add_id( | |
459 | validate_id(state, element_id), | |
460 | id_category::explicit_id); | |
461 | ||
462 | write_bridgehead(state, level, | |
463 | content.get_encoded(), anchor, self_linked_headers); | |
464 | } | |
465 | else if (state.document.compatibility_version() >= 106u) | |
466 | { | |
467 | // Generate ids for 1.6+ | |
468 | ||
469 | std::string anchor = state.document.add_id( | |
470 | detail::make_identifier(content.get_quickbook()), | |
471 | id_category::generated_heading); | |
472 | ||
473 | write_bridgehead(state, level, | |
474 | content.get_encoded(), anchor, self_linked_headers); | |
475 | } | |
476 | else | |
477 | { | |
478 | // Generate ids that are compatible with older versions of quickbook. | |
479 | ||
480 | // Older versions of quickbook used the generated boostbook, but | |
481 | // we only have an intermediate version which can contain id | |
482 | // placeholders. So to generate the ids they must be replaced | |
483 | // by the ids that the older versions would have used - i.e. the | |
484 | // unresolved ids. | |
485 | // | |
486 | // Note that this doesn't affect the actual boostbook generated for | |
487 | // the content, it's just used to generate this id. | |
488 | ||
489 | std::string id = detail::make_identifier( | |
490 | state.document.replace_placeholders_with_unresolved_ids( | |
491 | content.get_encoded())); | |
492 | ||
493 | if (generic || state.document.compatibility_version() >= 103) { | |
494 | std::string anchor = | |
495 | state.document.add_id(id, id_category::generated_heading); | |
496 | ||
497 | write_bridgehead(state, level, | |
498 | content.get_encoded(), anchor, self_linked_headers); | |
499 | } | |
500 | else { | |
501 | std::string anchor = | |
502 | state.document.old_style_id(id, id_category::generated_heading); | |
503 | ||
504 | write_bridgehead(state, level, | |
505 | content.get_encoded(), anchor, false); | |
506 | } | |
507 | } | |
508 | } | |
509 | ||
510 | void simple_phrase_action::operator()(char mark) const | |
511 | { | |
512 | write_anchors(state, state.phrase); | |
513 | ||
514 | int tag = | |
515 | mark == '*' ? phrase_tags::bold : | |
516 | mark == '/' ? phrase_tags::italic : | |
517 | mark == '_' ? phrase_tags::underline : | |
518 | mark == '=' ? phrase_tags::teletype : | |
519 | 0; | |
520 | ||
521 | assert(tag != 0); | |
522 | detail::markup markup = detail::get_markup(tag); | |
523 | ||
524 | value_consumer values = state.values.release(); | |
525 | value content = values.consume(); | |
526 | values.finish(); | |
527 | ||
528 | state.phrase << markup.pre; | |
529 | state.phrase << content.get_encoded(); | |
530 | state.phrase << markup.post; | |
531 | } | |
532 | ||
533 | bool cond_phrase_push::start() | |
534 | { | |
535 | value_consumer values = state.values.release(); | |
536 | ||
537 | saved_conditional = state.conditional; | |
538 | ||
539 | if (saved_conditional) | |
540 | { | |
b32b8144 FG |
541 | bool positive = values.consume().get_quickbook().empty(); |
542 | quickbook::string_view macro1 = values.consume().get_quickbook(); | |
7c673cae FG |
543 | std::string macro(macro1.begin(), macro1.end()); |
544 | ||
b32b8144 FG |
545 | state.conditional = |
546 | (bool)find(state.macro, macro.c_str()) == positive; | |
7c673cae FG |
547 | |
548 | if (!state.conditional) { | |
549 | state.push_output(); | |
550 | state.anchors.swap(anchors); | |
551 | } | |
552 | } | |
553 | ||
554 | return true; | |
555 | } | |
556 | ||
557 | void cond_phrase_push::cleanup() | |
558 | { | |
559 | if (saved_conditional && !state.conditional) | |
560 | { | |
561 | state.pop_output(); | |
562 | state.anchors.swap(anchors); | |
563 | } | |
564 | ||
565 | state.conditional = saved_conditional; | |
566 | } | |
567 | ||
568 | void state::start_list(char mark) | |
569 | { | |
570 | push_tagged_source_mode(source_mode_next); | |
571 | source_mode_next = 0; | |
572 | ||
573 | write_anchors(*this, (in_list ? phrase : out)); | |
574 | assert(mark == '*' || mark == '#'); | |
575 | push_output(); | |
576 | out << ((mark == '#') ? "<orderedlist>\n" : "<itemizedlist>\n"); | |
577 | in_list = true; | |
578 | } | |
579 | ||
580 | void state::end_list(char mark) | |
581 | { | |
582 | write_anchors(*this, out); | |
583 | assert(mark == '*' || mark == '#'); | |
584 | out << ((mark == '#') ? "\n</orderedlist>" : "\n</itemizedlist>"); | |
585 | ||
586 | std::string list_output; | |
587 | out.swap(list_output); | |
588 | ||
589 | pop_output(); | |
590 | ||
591 | (in_list ? phrase : out) << list_output; | |
592 | ||
593 | pop_tagged_source_mode(); | |
594 | } | |
595 | ||
596 | void state::start_list_item() | |
597 | { | |
598 | out << "<listitem>"; | |
599 | write_anchors(*this, phrase); | |
600 | } | |
601 | ||
602 | void state::end_list_item() | |
603 | { | |
604 | write_anchors(*this, phrase); | |
605 | paragraph_action para(*this); | |
606 | para(); | |
607 | out << "</listitem>"; | |
608 | } | |
609 | ||
610 | namespace | |
611 | { | |
612 | bool parse_template(value const&, quickbook::state& state, | |
613 | bool is_attribute_template = false); | |
614 | } | |
615 | ||
616 | void state::start_callouts() | |
617 | { | |
618 | ++callout_depth; | |
619 | } | |
620 | ||
621 | std::string state::add_callout(value v) | |
622 | { | |
623 | std::string callout_id1 = document.add_id("c", id_category::numbered); | |
624 | std::string callout_id2 = document.add_id("c", id_category::numbered); | |
625 | ||
626 | callouts.insert(encoded_value(callout_id1)); | |
627 | callouts.insert(encoded_value(callout_id2)); | |
628 | callouts.insert(v); | |
629 | ||
630 | std::string code; | |
631 | code += "<co id=\"" + callout_id1 + "\" "; | |
632 | code += "linkends=\"" + callout_id2 + "\" />"; | |
633 | ||
634 | return code; | |
635 | } | |
636 | ||
637 | std::string state::end_callouts() | |
638 | { | |
639 | assert(callout_depth > 0); | |
640 | std::string block; | |
641 | ||
642 | --callout_depth; | |
643 | if (callout_depth > 0) return block; | |
644 | ||
645 | value_consumer c = callouts.release(); | |
646 | if (!c.check()) return block; | |
647 | ||
648 | block += "<calloutlist>"; | |
649 | while (c.check()) | |
650 | { | |
651 | std::string callout_id1 = c.consume().get_encoded(); | |
652 | std::string callout_id2 = c.consume().get_encoded(); | |
653 | value callout_body = c.consume(); | |
654 | ||
655 | std::string callout_value; | |
656 | ||
657 | { | |
658 | state_save save(*this, state_save::scope_all); | |
659 | ++template_depth; | |
660 | ||
661 | bool r = parse_template(callout_body, *this); | |
662 | ||
663 | if(!r) | |
664 | { | |
665 | detail::outerr(callout_body.get_file(), callout_body.get_position()) | |
666 | << "Expanding callout." << std::endl | |
667 | << "------------------begin------------------" << std::endl | |
668 | << callout_body.get_quickbook() | |
669 | << std::endl | |
670 | << "------------------end--------------------" << std::endl | |
671 | ; | |
672 | ++error_count; | |
673 | } | |
674 | ||
675 | out.swap(callout_value); | |
676 | } | |
677 | ||
678 | block += "<callout arearefs=\"" + callout_id1 + "\" "; | |
679 | block += "id=\"" + callout_id2 + "\">"; | |
680 | block += callout_value; | |
681 | block += "</callout>"; | |
682 | } | |
683 | block += "</calloutlist>"; | |
684 | ||
685 | return block; | |
686 | } | |
687 | ||
b32b8144 | 688 | void list_action(quickbook::state& state, value list) |
7c673cae FG |
689 | { |
690 | write_anchors(state, state.out); | |
691 | ||
692 | detail::markup markup = detail::get_markup(list.get_tag()); | |
693 | ||
694 | state.out << markup.pre; | |
695 | ||
696 | BOOST_FOREACH(value item, list) | |
697 | { | |
698 | state.out << "<listitem>"; | |
699 | state.out << item.get_encoded(); | |
700 | state.out << "</listitem>"; | |
701 | } | |
702 | ||
703 | state.out << markup.post; | |
704 | } | |
705 | ||
706 | void anchor_action(quickbook::state& state, value anchor) | |
707 | { | |
708 | value_consumer values = anchor; | |
709 | value anchor_id = values.consume(); | |
710 | // Note: anchor_id is never encoded as boostbook. If it | |
711 | // is encoded, it's just things like escapes. | |
712 | add_anchor(state, validate_id(state, anchor_id)); | |
713 | values.finish(); | |
714 | } | |
715 | ||
716 | void do_macro_action::operator()(std::string const& str) const | |
717 | { | |
718 | write_anchors(state, state.phrase); | |
719 | ||
720 | if (str == quickbook_get_date) | |
721 | { | |
722 | char strdate[64]; | |
723 | strftime(strdate, sizeof(strdate), "%Y-%b-%d", current_time); | |
724 | state.phrase << strdate; | |
725 | } | |
726 | else if (str == quickbook_get_time) | |
727 | { | |
728 | char strdate[64]; | |
729 | strftime(strdate, sizeof(strdate), "%I:%M:%S %p", current_time); | |
730 | state.phrase << strdate; | |
731 | } | |
732 | else | |
733 | { | |
734 | state.phrase << str; | |
735 | } | |
736 | } | |
737 | ||
738 | void raw_char_action::operator()(char ch) const | |
739 | { | |
740 | state.phrase << ch; | |
741 | } | |
742 | ||
743 | void raw_char_action::operator()(parse_iterator first, parse_iterator last) const | |
744 | { | |
745 | while (first != last) | |
746 | state.phrase << *first++; | |
747 | } | |
748 | ||
749 | void source_mode_action(quickbook::state& state, value source_mode) | |
750 | { | |
751 | state.change_source_mode(source_mode.get_tag()); | |
752 | } | |
753 | ||
754 | void next_source_mode_action(quickbook::state& state, value source_mode) | |
755 | { | |
756 | value_consumer values = source_mode; | |
757 | state.source_mode_next_pos = values.consume(); | |
758 | state.source_mode_next = values.consume().get_int(); | |
759 | values.finish(); | |
760 | } | |
761 | ||
762 | void code_action(quickbook::state& state, value code_block) | |
763 | { | |
764 | int code_tag = code_block.get_tag(); | |
765 | ||
766 | value_consumer values = code_block; | |
b32b8144 | 767 | quickbook::string_view code_value = values.consume().get_quickbook(); |
7c673cae FG |
768 | values.finish(); |
769 | ||
770 | bool inline_code = code_tag == code_tags::inline_code || | |
771 | (code_tag == code_tags::inline_code_block && qbk_version_n < 106u); | |
772 | bool block = code_tag != code_tags::inline_code; | |
773 | ||
774 | source_mode_type source_mode = state.source_mode_next ? | |
775 | state.source_mode_next : state.current_source_mode().source_mode; | |
776 | state.source_mode_next = 0; | |
777 | ||
778 | if (inline_code) { | |
779 | write_anchors(state, state.phrase); | |
780 | } | |
781 | else { | |
782 | paragraph_action para(state); | |
783 | para(); | |
784 | write_anchors(state, state.out); | |
785 | } | |
786 | ||
787 | if (block) { | |
788 | // preprocess the code section to remove the initial indentation | |
789 | mapped_file_builder mapped; | |
790 | mapped.start(state.current_file); | |
791 | mapped.unindent_and_add(code_value); | |
792 | ||
793 | file_ptr f = mapped.release(); | |
794 | ||
795 | if (f->source().empty()) | |
796 | return; // Nothing left to do here. The program is empty. | |
797 | ||
798 | if (qbk_version_n >= 107u) state.start_callouts(); | |
799 | ||
800 | parse_iterator first_(f->source().begin()); | |
801 | parse_iterator last_(f->source().end()); | |
802 | ||
803 | file_ptr saved_file = f; | |
804 | boost::swap(state.current_file, saved_file); | |
805 | ||
806 | // print the code with syntax coloring | |
807 | // | |
808 | // We must not place a \n after the <programlisting> tag | |
809 | // otherwise PDF output starts code blocks with a blank line: | |
810 | state.phrase << "<programlisting>"; | |
811 | syntax_highlight(first_, last_, state, source_mode, block); | |
812 | state.phrase << "</programlisting>\n"; | |
813 | ||
814 | boost::swap(state.current_file, saved_file); | |
815 | ||
816 | if (qbk_version_n >= 107u) state.phrase << state.end_callouts(); | |
817 | ||
818 | if (!inline_code) { | |
819 | state.out << state.phrase.str(); | |
820 | state.phrase.clear(); | |
821 | } | |
822 | } | |
823 | else { | |
824 | parse_iterator first_(code_value.begin()); | |
825 | parse_iterator last_(code_value.end()); | |
826 | ||
827 | state.phrase << "<code>"; | |
828 | syntax_highlight(first_, last_, state, source_mode, block); | |
829 | state.phrase << "</code>"; | |
830 | } | |
831 | } | |
832 | ||
833 | void plain_char_action::operator()(char ch) const | |
834 | { | |
835 | write_anchors(state, state.phrase); | |
836 | ||
837 | detail::print_char(ch, state.phrase.get()); | |
838 | } | |
839 | ||
840 | void plain_char_action::operator()(parse_iterator first, parse_iterator last) const | |
841 | { | |
842 | write_anchors(state, state.phrase); | |
843 | ||
844 | while (first != last) | |
845 | detail::print_char(*first++, state.phrase.get()); | |
846 | } | |
847 | ||
848 | void escape_unicode_action::operator()(parse_iterator first, parse_iterator last) const | |
849 | { | |
850 | write_anchors(state, state.phrase); | |
851 | ||
852 | while(first != last && *first == '0') ++first; | |
853 | ||
854 | // Just ignore \u0000 | |
855 | // Maybe I should issue a warning? | |
856 | if(first == last) return; | |
857 | ||
858 | std::string hex_digits(first, last); | |
859 | ||
860 | if(hex_digits.size() == 2 && *first > '0' && *first <= '7') { | |
861 | using namespace std; | |
b32b8144 FG |
862 | detail::print_char( |
863 | (char) strtol(hex_digits.c_str(), 0, 16), | |
7c673cae FG |
864 | state.phrase.get()); |
865 | } | |
866 | else { | |
867 | state.phrase << "&#x" << hex_digits << ";"; | |
868 | } | |
869 | } | |
870 | ||
871 | void write_plain_text(std::ostream& out, value const& v) | |
872 | { | |
873 | if (v.is_encoded()) | |
874 | { | |
875 | detail::print_string(v.get_encoded(), out); | |
876 | } | |
877 | else { | |
b32b8144 FG |
878 | quickbook::string_view value = v.get_quickbook(); |
879 | for(string_iterator | |
7c673cae FG |
880 | first = value.begin(), last = value.end(); |
881 | first != last; ++first) | |
882 | { | |
883 | if (*first == '\\' && ++first == last) break; | |
884 | detail::print_char(*first, out); | |
885 | } | |
886 | } | |
887 | } | |
888 | ||
889 | void image_action(quickbook::state& state, value image) | |
890 | { | |
891 | write_anchors(state, state.phrase); | |
892 | ||
893 | // Note: attributes are never encoded as boostbook, if they're | |
894 | // encoded, it's just things like escapes. | |
895 | typedef std::map<std::string, value> attribute_map; | |
896 | attribute_map attributes; | |
897 | ||
898 | value_consumer values = image; | |
899 | attributes["fileref"] = values.consume(); | |
900 | ||
901 | BOOST_FOREACH(value pair_, values) | |
902 | { | |
903 | value_consumer pair = pair_; | |
904 | value name = pair.consume(); | |
905 | value value = pair.consume(); | |
906 | std::string name_str(name.get_quickbook().begin(), | |
907 | name.get_quickbook().end()); | |
908 | pair.finish(); | |
909 | if(!attributes.insert(std::make_pair(name_str, value)).second) | |
910 | { | |
911 | detail::outwarn(name.get_file(), name.get_position()) | |
912 | << "Duplicate image attribute: " | |
913 | << name.get_quickbook() | |
914 | << std::endl; | |
915 | } | |
916 | } | |
917 | ||
918 | values.finish(); | |
919 | ||
920 | // Find the file basename and extension. | |
921 | // | |
922 | // Not using Boost.Filesystem because I want to stay in UTF-8. | |
923 | // Need to think about uri encoding. | |
924 | ||
925 | std::string fileref = attributes["fileref"].is_encoded() ? | |
926 | attributes["fileref"].get_encoded() : | |
b32b8144 | 927 | attributes["fileref"].get_quickbook().to_s(); |
7c673cae FG |
928 | |
929 | // Check for windows paths, then convert. | |
930 | // A bit crude, but there you go. | |
931 | ||
932 | if(fileref.find('\\') != std::string::npos) | |
933 | { | |
934 | (qbk_version_n >= 106u ? | |
935 | detail::outerr(attributes["fileref"].get_file(), attributes["fileref"].get_position()) : | |
936 | detail::outwarn(attributes["fileref"].get_file(), attributes["fileref"].get_position())) | |
937 | << "Image path isn't portable: '" | |
938 | << fileref | |
939 | << "'" | |
940 | << std::endl; | |
941 | if (qbk_version_n >= 106u) ++state.error_count; | |
942 | } | |
943 | ||
944 | boost::replace(fileref, '\\', '/'); | |
945 | ||
946 | // Find the file basename and extension. | |
947 | // | |
948 | // Not using Boost.Filesystem because I want to stay in UTF-8. | |
949 | // Need to think about uri encoding. | |
950 | ||
951 | std::string::size_type pos; | |
952 | std::string stem, extension; | |
953 | ||
954 | pos = fileref.rfind('/'); | |
955 | stem = pos == std::string::npos ? | |
956 | fileref : | |
957 | fileref.substr(pos + 1); | |
958 | ||
959 | pos = stem.rfind('.'); | |
960 | if (pos != std::string::npos) | |
961 | { | |
962 | extension = stem.substr(pos + 1); | |
963 | stem = stem.substr(0, pos); | |
964 | } | |
965 | ||
966 | // Extract the alt tag, to use as a text description. | |
967 | // Or if there isn't one, use the stem of the file name. | |
968 | ||
969 | attribute_map::iterator alt_pos = attributes.find("alt"); | |
970 | quickbook::value alt_text = | |
971 | alt_pos != attributes.end() ? alt_pos->second : | |
972 | qbk_version_n < 106u ? encoded_value(stem) : | |
973 | quickbook::value(); | |
974 | attributes.erase("alt"); | |
975 | ||
976 | if(extension == "svg") | |
977 | { | |
978 | // | |
979 | // SVG's need special handling: | |
980 | // | |
981 | // 1) We must set the "format" attribute, otherwise | |
982 | // HTML generation produces code that will not display | |
983 | // the image at all. | |
984 | // 2) We need to set the "contentwidth" and "contentdepth" | |
985 | // attributes, otherwise the image will be displayed inside | |
986 | // a tiny box with scrollbars (Firefox), or else cropped to | |
987 | // fit in a tiny box (IE7). | |
988 | // | |
989 | ||
990 | attributes.insert(attribute_map::value_type("format", | |
991 | encoded_value("SVG"))); | |
992 | ||
993 | // | |
994 | // Image paths are relative to the html subdirectory: | |
995 | // | |
996 | fs::path img = detail::generic_to_path(fileref); | |
997 | if (!img.has_root_directory()) | |
998 | img = quickbook::image_location / img; // relative path | |
999 | ||
1000 | // | |
1001 | // Now load the SVG file: | |
1002 | // | |
1003 | std::string svg_text; | |
1004 | if (state.dependencies.add_dependency(img)) { | |
1005 | fs::ifstream fs(img); | |
1006 | std::stringstream buffer; | |
1007 | buffer << fs.rdbuf(); | |
1008 | svg_text = buffer.str(); | |
1009 | } | |
1010 | ||
1011 | // | |
1012 | // Extract the svg header from the file: | |
1013 | // | |
1014 | std::string::size_type a, b; | |
1015 | a = svg_text.find("<svg"); | |
1016 | b = svg_text.find('>', a); | |
1017 | svg_text = (a == std::string::npos) ? "" : svg_text.substr(a, b - a); | |
1018 | // | |
1019 | // Now locate the "width" and "height" attributes | |
1020 | // and borrow their values: | |
1021 | // | |
1022 | a = svg_text.find("width"); | |
1023 | a = svg_text.find('=', a); | |
1024 | a = svg_text.find('\"', a); | |
1025 | b = svg_text.find('\"', a + 1); | |
1026 | if(a != std::string::npos) | |
1027 | { | |
1028 | attributes.insert(std::make_pair( | |
1029 | "contentwidth", encoded_value(std::string( | |
b32b8144 FG |
1030 | boost::next(svg_text.begin(), a + 1), |
1031 | boost::next(svg_text.begin(), b))) | |
7c673cae FG |
1032 | )); |
1033 | } | |
1034 | a = svg_text.find("height"); | |
1035 | a = svg_text.find('=', a); | |
1036 | a = svg_text.find('\"', a); | |
1037 | b = svg_text.find('\"', a + 1); | |
1038 | if(a != std::string::npos) | |
1039 | { | |
1040 | attributes.insert(std::make_pair( | |
1041 | "contentdepth", encoded_value(std::string( | |
b32b8144 FG |
1042 | boost::next(svg_text.begin(), a + 1), |
1043 | boost::next(svg_text.begin(), b))) | |
7c673cae FG |
1044 | )); |
1045 | } | |
1046 | } | |
1047 | ||
1048 | state.phrase << "<inlinemediaobject>"; | |
1049 | ||
1050 | state.phrase << "<imageobject><imagedata"; | |
1051 | ||
1052 | BOOST_FOREACH(attribute_map::value_type const& attr, attributes) | |
1053 | { | |
1054 | state.phrase << " " << attr.first << "=\""; | |
1055 | write_plain_text(state.phrase.get(), attr.second); | |
1056 | state.phrase << "\""; | |
1057 | } | |
1058 | ||
1059 | state.phrase << "></imagedata></imageobject>"; | |
1060 | ||
1061 | // Add a textobject containing the alt tag from earlier. | |
1062 | // This will be used for the alt tag in html. | |
1063 | if (alt_text.check()) { | |
1064 | state.phrase << "<textobject><phrase>"; | |
1065 | write_plain_text(state.phrase.get(), alt_text); | |
1066 | state.phrase << "</phrase></textobject>"; | |
1067 | } | |
1068 | ||
1069 | state.phrase << "</inlinemediaobject>"; | |
1070 | } | |
1071 | ||
1072 | void macro_definition_action(quickbook::state& state, quickbook::value macro_definition) | |
1073 | { | |
1074 | value_consumer values = macro_definition; | |
b32b8144 | 1075 | std::string macro_id = values.consume().get_quickbook().to_s(); |
7c673cae FG |
1076 | value phrase_value = values.optional_consume(); |
1077 | std::string phrase; | |
1078 | if (phrase_value.check()) phrase = phrase_value.get_encoded(); | |
1079 | values.finish(); | |
1080 | ||
1081 | std::string* existing_macro = | |
1082 | boost::spirit::classic::find(state.macro, macro_id.c_str()); | |
1083 | quickbook::ignore_variable(&existing_macro); | |
1084 | ||
1085 | if (existing_macro) | |
1086 | { | |
1087 | if (qbk_version_n < 106) return; | |
1088 | ||
1089 | // Do this if you're using spirit's TST. | |
1090 | // | |
1091 | // *existing_macro = phrase; | |
1092 | // return; | |
1093 | } | |
1094 | ||
1095 | state.macro.add( | |
1096 | macro_id.begin() | |
1097 | , macro_id.end() | |
1098 | , phrase); | |
1099 | } | |
1100 | ||
1101 | void template_body_action(quickbook::state& state, quickbook::value template_definition) | |
1102 | { | |
1103 | value_consumer values = template_definition; | |
b32b8144 | 1104 | std::string identifier = values.consume().get_quickbook().to_s(); |
7c673cae FG |
1105 | |
1106 | std::vector<std::string> template_values; | |
1107 | BOOST_FOREACH(value const& p, values.consume()) { | |
b32b8144 | 1108 | template_values.push_back(p.get_quickbook().to_s()); |
7c673cae FG |
1109 | } |
1110 | ||
1111 | BOOST_ASSERT(values.check(template_tags::block) || values.check(template_tags::phrase)); | |
1112 | value body = values.consume(); | |
1113 | BOOST_ASSERT(!values.check()); | |
1114 | ||
1115 | if (!state.templates.add( | |
1116 | template_symbol( | |
1117 | identifier, | |
1118 | template_values, | |
1119 | body, | |
1120 | &state.templates.top_scope()))) | |
1121 | { | |
1122 | detail::outwarn(body.get_file(), body.get_position()) | |
1123 | << "Template Redefinition: " << identifier << std::endl; | |
1124 | ++state.error_count; | |
1125 | } | |
1126 | } | |
1127 | ||
1128 | namespace | |
1129 | { | |
1130 | string_iterator find_first_seperator(string_iterator begin, string_iterator end) | |
1131 | { | |
1132 | if(qbk_version_n < 105) { | |
1133 | for(;begin != end; ++begin) | |
1134 | { | |
1135 | switch(*begin) | |
1136 | { | |
1137 | case ' ': | |
1138 | case '\t': | |
1139 | case '\n': | |
1140 | case '\r': | |
1141 | return begin; | |
1142 | default: | |
1143 | break; | |
1144 | } | |
1145 | } | |
1146 | } | |
1147 | else { | |
1148 | unsigned int depth = 0; | |
1149 | ||
1150 | for(;begin != end; ++begin) | |
1151 | { | |
1152 | switch(*begin) | |
1153 | { | |
1154 | case '[': | |
1155 | ++depth; | |
1156 | break; | |
1157 | case '\\': | |
1158 | if(++begin == end) return begin; | |
1159 | break; | |
1160 | case ']': | |
1161 | if (depth > 0) --depth; | |
1162 | break; | |
1163 | case ' ': | |
1164 | case '\t': | |
1165 | case '\n': | |
1166 | case '\r': | |
1167 | if (depth == 0) return begin; | |
1168 | default: | |
1169 | break; | |
1170 | } | |
1171 | } | |
1172 | } | |
1173 | ||
1174 | return begin; | |
1175 | } | |
1176 | ||
1177 | std::pair<string_iterator, string_iterator> find_seperator(string_iterator begin, string_iterator end) | |
1178 | { | |
1179 | string_iterator first = begin = find_first_seperator(begin, end); | |
1180 | ||
1181 | for(;begin != end; ++begin) | |
1182 | { | |
1183 | switch(*begin) | |
1184 | { | |
1185 | case ' ': | |
1186 | case '\t': | |
1187 | case '\n': | |
1188 | case '\r': | |
1189 | break; | |
1190 | default: | |
1191 | return std::make_pair(first, begin); | |
1192 | } | |
1193 | } | |
1194 | ||
1195 | return std::make_pair(first, begin); | |
1196 | } | |
1197 | ||
1198 | void break_arguments( | |
1199 | std::vector<value>& args | |
1200 | , std::vector<std::string> const& params | |
b32b8144 | 1201 | , fs::path const& /* filename */ |
7c673cae FG |
1202 | ) |
1203 | { | |
1204 | // Quickbook 1.4-: If there aren't enough parameters seperated by | |
1205 | // '..' then seperate the last parameter using | |
1206 | // whitespace. | |
1207 | // Quickbook 1.5+: If '..' isn't used to seperate the parameters | |
1208 | // then use whitespace to separate them | |
1209 | // (2 = template name + argument). | |
1210 | ||
b32b8144 | 1211 | if (qbk_version_n < 105 ? args.size() : args.size() == 1) |
7c673cae FG |
1212 | { |
1213 | ||
1214 | while (args.size() < params.size()) | |
1215 | { | |
1216 | // Try to break the last argument at the first space found | |
1217 | // and push it into the back of args. Do this | |
1218 | // recursively until we have all the expected number of | |
1219 | // arguments, or if there are no more spaces left. | |
1220 | ||
1221 | value last_arg = args.back(); | |
1222 | string_iterator begin = last_arg.get_quickbook().begin(); | |
1223 | string_iterator end = last_arg.get_quickbook().end(); | |
1224 | ||
1225 | std::pair<string_iterator, string_iterator> pos = | |
1226 | find_seperator(begin, end); | |
1227 | if (pos.second == end) break; | |
1228 | value new_arg( | |
1229 | qbk_value(last_arg.get_file(), | |
1230 | pos.second, end, template_tags::phrase)); | |
1231 | ||
1232 | args.back() = qbk_value(last_arg.get_file(), | |
1233 | begin, pos.first, last_arg.get_tag()); | |
1234 | args.push_back(new_arg); | |
1235 | } | |
1236 | } | |
1237 | } | |
1238 | ||
1239 | std::pair<bool, std::vector<std::string>::const_iterator> | |
1240 | get_arguments( | |
1241 | std::vector<value> const& args | |
1242 | , std::vector<std::string> const& params | |
1243 | , template_scope const& scope | |
1244 | , string_iterator first | |
1245 | , quickbook::state& state | |
1246 | ) | |
1247 | { | |
1248 | std::vector<value>::const_iterator arg = args.begin(); | |
1249 | std::vector<std::string>::const_iterator tpl = params.begin(); | |
1250 | std::vector<std::string> empty_params; | |
1251 | ||
1252 | // Store each of the argument passed in as local templates: | |
1253 | while (arg != args.end()) | |
1254 | { | |
1255 | if (!state.templates.add( | |
1256 | template_symbol(*tpl, empty_params, *arg, &scope))) | |
1257 | { | |
1258 | detail::outerr(state.current_file, first) | |
1259 | << "Duplicate Symbol Found" << std::endl; | |
1260 | ++state.error_count; | |
1261 | return std::make_pair(false, tpl); | |
1262 | } | |
1263 | ++arg; ++tpl; | |
1264 | } | |
1265 | return std::make_pair(true, tpl); | |
1266 | } | |
1267 | ||
1268 | bool parse_template( | |
1269 | value const& content | |
1270 | , quickbook::state& state | |
1271 | , bool is_attribute_template | |
1272 | ) | |
1273 | { | |
1274 | file_ptr saved_current_file = state.current_file; | |
1275 | ||
1276 | state.current_file = content.get_file(); | |
b32b8144 | 1277 | quickbook::string_view source = content.get_quickbook(); |
7c673cae FG |
1278 | |
1279 | parse_iterator first(source.begin()); | |
1280 | parse_iterator last(source.end()); | |
1281 | ||
1282 | bool r = cl::parse(first, last, | |
1283 | is_attribute_template ? | |
1284 | state.grammar().attribute_template_body : | |
1285 | content.get_tag() == template_tags::phrase ? | |
1286 | state.grammar().inline_phrase : | |
1287 | state.grammar().block_start | |
1288 | ).full; | |
1289 | ||
1290 | boost::swap(state.current_file, saved_current_file); | |
1291 | ||
1292 | return r; | |
1293 | } | |
1294 | } | |
1295 | ||
1296 | void call_template(quickbook::state& state, | |
1297 | template_symbol const* symbol, | |
1298 | std::vector<value> const& args, | |
1299 | string_iterator first, | |
1300 | bool is_attribute_template = false) | |
1301 | { | |
1302 | bool is_block = symbol->content.get_tag() != template_tags::phrase; | |
1303 | assert(!(is_attribute_template && is_block)); | |
1304 | ||
1305 | quickbook::paragraph_action paragraph_action(state); | |
1306 | ||
1307 | // Finish off any existing paragraphs. | |
1308 | if (is_block) paragraph_action(); | |
1309 | ||
1310 | // If this template contains already encoded text, then just | |
1311 | // write it out, without going through any of the rigamarole. | |
1312 | ||
1313 | if (symbol->content.is_encoded()) | |
1314 | { | |
1315 | (is_block ? state.out : state.phrase) << symbol->content.get_encoded(); | |
1316 | return; | |
1317 | } | |
1318 | ||
1319 | // The template arguments should have the scope that the template was | |
1320 | // called from, not the template's own scope. | |
1321 | // | |
1322 | // Note that for quickbook 1.4- this value is just ignored when the | |
1323 | // arguments are expanded. | |
1324 | template_scope const& call_scope = state.templates.top_scope(); | |
1325 | ||
1326 | { | |
1327 | state_save save(state, state_save::scope_callables); | |
1328 | std::string save_block; | |
1329 | std::string save_phrase; | |
1330 | ||
1331 | state.templates.start_template(symbol); | |
1332 | ||
1333 | qbk_version_n = symbol->content.get_file()->version(); | |
1334 | ||
1335 | ++state.template_depth; | |
1336 | if (state.template_depth > state.max_template_depth) | |
1337 | { | |
1338 | detail::outerr(state.current_file, first) | |
1339 | << "Infinite loop detected" << std::endl; | |
1340 | ++state.error_count; | |
1341 | return; | |
1342 | } | |
1343 | ||
1344 | // Store the current section level so that we can ensure that | |
1345 | // [section] and [endsect] tags in the template are balanced. | |
1346 | state.min_section_level = state.document.section_level(); | |
1347 | ||
1348 | /////////////////////////////////// | |
1349 | // Prepare the arguments as local templates | |
1350 | bool get_arg_result; | |
1351 | std::vector<std::string>::const_iterator tpl; | |
1352 | boost::tie(get_arg_result, tpl) = | |
1353 | get_arguments(args, symbol->params, call_scope, first, state); | |
1354 | ||
1355 | if (!get_arg_result) | |
1356 | { | |
1357 | return; | |
1358 | } | |
1359 | ||
1360 | /////////////////////////////////// | |
1361 | // parse the template body: | |
1362 | ||
1363 | if (symbol->content.get_file()->version() < 107u) { | |
1364 | state.out.swap(save_block); | |
1365 | state.phrase.swap(save_phrase); | |
1366 | } | |
1367 | ||
1368 | if (!parse_template(symbol->content, state, is_attribute_template)) | |
1369 | { | |
1370 | detail::outerr(state.current_file, first) | |
1371 | << "Expanding " | |
1372 | << (is_block ? "block" : "phrase") | |
1373 | << " template: " << symbol->identifier << "\n\n" | |
1374 | << "------------------begin------------------\n" | |
1375 | << symbol->content.get_quickbook() | |
1376 | << "------------------end--------------------\n" | |
1377 | << std::endl; | |
1378 | ++state.error_count; | |
1379 | return; | |
1380 | } | |
1381 | ||
1382 | if (state.document.section_level() != state.min_section_level) | |
1383 | { | |
1384 | detail::outerr(state.current_file, first) | |
1385 | << "Mismatched sections in template " | |
1386 | << symbol->identifier | |
1387 | << std::endl; | |
1388 | ++state.error_count; | |
1389 | return; | |
1390 | } | |
1391 | ||
1392 | if (symbol->content.get_file()->version() < 107u) { | |
1393 | state.out.swap(save_block); | |
1394 | state.phrase.swap(save_phrase); | |
1395 | ||
1396 | if(is_block || !save_block.empty()) { | |
1397 | paragraph_action(); | |
1398 | state.out << save_block; | |
1399 | state.phrase << save_phrase; | |
1400 | paragraph_action(); | |
1401 | } | |
1402 | else { | |
1403 | state.phrase << save_phrase; | |
1404 | } | |
1405 | } | |
1406 | else | |
1407 | { | |
1408 | if (is_block) paragraph_action(); | |
1409 | } | |
1410 | } | |
1411 | } | |
1412 | ||
1413 | void call_code_snippet(quickbook::state& state, | |
1414 | template_symbol const* symbol, | |
1415 | string_iterator first) | |
1416 | { | |
1417 | assert(symbol->params.size() == 0); | |
1418 | std::vector<value> args; | |
1419 | ||
1420 | // Create a fake symbol for call_template | |
1421 | template_symbol t( | |
1422 | symbol->identifier, | |
1423 | symbol->params, | |
1424 | symbol->content, | |
1425 | symbol->lexical_parent); | |
1426 | ||
1427 | state.start_callouts(); | |
1428 | call_template(state, &t, args, first); | |
1429 | state.out << state.end_callouts(); | |
1430 | } | |
1431 | ||
1432 | void do_template_action(quickbook::state& state, value template_list, | |
1433 | string_iterator first) | |
1434 | { | |
1435 | bool const is_attribute_template = | |
1436 | template_list.get_tag() == template_tags::attribute_template; | |
1437 | ||
1438 | // Get the arguments | |
1439 | value_consumer values = template_list; | |
1440 | ||
1441 | bool template_escape = values.check(template_tags::escape); | |
1442 | if(template_escape) values.consume(); | |
1443 | ||
b32b8144 | 1444 | std::string identifier = values.consume(template_tags::identifier).get_quickbook().to_s(); |
7c673cae FG |
1445 | |
1446 | std::vector<value> args; | |
1447 | ||
1448 | BOOST_FOREACH(value arg, values) | |
1449 | { | |
1450 | args.push_back(arg); | |
1451 | } | |
1452 | ||
1453 | values.finish(); | |
1454 | ||
1455 | template_symbol const* symbol = state.templates.find(identifier); | |
1456 | BOOST_ASSERT(symbol); | |
1457 | ||
1458 | // Deal with escaped templates. | |
1459 | ||
1460 | if (template_escape) | |
1461 | { | |
1462 | if (!args.empty()) | |
1463 | { | |
1464 | detail::outerr(state.current_file, first) | |
1465 | << "Arguments for escaped template." | |
1466 | <<std::endl; | |
1467 | ++state.error_count; | |
1468 | } | |
1469 | ||
1470 | if (symbol->content.is_encoded()) | |
1471 | { | |
1472 | state.phrase << symbol->content.get_encoded(); | |
1473 | } | |
1474 | else | |
1475 | { | |
1476 | state.phrase << symbol->content.get_quickbook(); | |
1477 | ||
1478 | /* | |
1479 | ||
1480 | This would surround the escaped template in escape | |
1481 | comments to indicate to the post-processor that it | |
1482 | isn't quickbook generated markup. But I'm not sure if | |
1483 | it would work. | |
1484 | ||
1485 | quickbook::detail::markup escape_markup | |
1486 | = detail::get_markup(phrase_tags::escape); | |
1487 | ||
1488 | state.phrase | |
1489 | << escape_markup.pre | |
1490 | << symbol->content.get_quickbook() | |
1491 | << escape_markup.post | |
1492 | ; | |
1493 | */ | |
1494 | } | |
1495 | ||
1496 | return; | |
1497 | } | |
1498 | ||
1499 | /////////////////////////////////// | |
1500 | // Check that attribute templates are phrase templates | |
1501 | ||
1502 | if (is_attribute_template && | |
1503 | symbol->content.get_tag() != template_tags::phrase) | |
1504 | { | |
1505 | detail::outerr(state.current_file, first) | |
1506 | << "Only phrase templates can be used in attribute values." | |
1507 | << std::endl; | |
1508 | ||
1509 | ++state.error_count; | |
1510 | return; | |
1511 | } | |
1512 | ||
1513 | /////////////////////////////////// | |
1514 | // Initialise the arguments | |
1515 | ||
1516 | switch(symbol->content.get_tag()) | |
1517 | { | |
1518 | case template_tags::block: | |
1519 | case template_tags::phrase: | |
1520 | // Break the arguments for a template | |
1521 | ||
1522 | break_arguments(args, symbol->params, state.current_file->path); | |
1523 | ||
1524 | if (args.size() != symbol->params.size()) | |
1525 | { | |
1526 | detail::outerr(state.current_file, first) | |
1527 | << "Invalid number of arguments passed. Expecting: " | |
1528 | << symbol->params.size() | |
1529 | << " argument(s), got: " | |
1530 | << args.size() | |
1531 | << " argument(s) instead." | |
1532 | << std::endl; | |
1533 | ||
1534 | ++state.error_count; | |
1535 | return; | |
1536 | } | |
1537 | ||
1538 | call_template(state, symbol, args, first, is_attribute_template); | |
1539 | break; | |
1540 | ||
1541 | case template_tags::snippet: | |
1542 | ||
1543 | if (!args.empty()) | |
1544 | { | |
1545 | detail::outerr(state.current_file, first) | |
1546 | << "Arguments for code snippet." | |
1547 | <<std::endl; | |
1548 | ++state.error_count; | |
1549 | ||
1550 | args.clear(); | |
1551 | } | |
1552 | ||
1553 | call_code_snippet(state, symbol, first); | |
1554 | break; | |
1555 | ||
1556 | default: | |
1557 | assert(0); | |
1558 | } | |
1559 | } | |
1560 | ||
1561 | void link_action(quickbook::state& state, value link) | |
1562 | { | |
1563 | write_anchors(state, state.phrase); | |
1564 | ||
1565 | detail::markup markup = detail::get_markup(link.get_tag()); | |
1566 | ||
1567 | value_consumer values = link; | |
1568 | value dst_value = values.consume(); | |
1569 | value content = values.consume(); | |
1570 | values.finish(); | |
1571 | ||
1572 | std::string dst; | |
1573 | ||
1574 | if (link.get_tag() == phrase_tags::link) { | |
1575 | dst = validate_id(state, dst_value); | |
1576 | } | |
1577 | else { | |
1578 | dst = get_attribute_value(state, dst_value); | |
1579 | ||
7c673cae FG |
1580 | if (link.get_tag() == phrase_tags::url) { |
1581 | dst = detail::partially_escape_uri(dst); | |
1582 | } | |
1583 | } | |
1584 | ||
1585 | state.phrase << markup.pre; | |
1586 | detail::print_string(dst, state.phrase.get()); | |
1587 | state.phrase << "\">"; | |
1588 | ||
1589 | if (content.empty()) | |
1590 | detail::print_string(dst, state.phrase.get()); | |
1591 | else | |
1592 | state.phrase << content.get_encoded(); | |
1593 | ||
1594 | state.phrase << markup.post; | |
1595 | } | |
1596 | ||
1597 | void variable_list_action(quickbook::state& state, value variable_list) | |
1598 | { | |
1599 | write_anchors(state, state.out); | |
1600 | ||
1601 | value_consumer values = variable_list; | |
b32b8144 | 1602 | std::string title = values.consume(table_tags::title).get_quickbook().to_s(); |
7c673cae FG |
1603 | |
1604 | state.out << "<variablelist>\n"; | |
1605 | ||
1606 | state.out << "<title>"; | |
1607 | detail::print_string(title, state.out.get()); | |
1608 | state.out << "</title>\n"; | |
1609 | ||
1610 | BOOST_FOREACH(value_consumer entry, values) { | |
1611 | state.out << "<varlistentry>"; | |
1612 | ||
1613 | if(entry.check()) { | |
1614 | state.out << "<term>"; | |
1615 | state.out << entry.consume().get_encoded(); | |
1616 | state.out << "</term>"; | |
1617 | } | |
1618 | ||
1619 | if(entry.check()) { | |
1620 | state.out << "<listitem>"; | |
1621 | BOOST_FOREACH(value phrase, entry) state.out << phrase.get_encoded(); | |
1622 | state.out << "</listitem>"; | |
1623 | } | |
1624 | ||
1625 | state.out << "</varlistentry>\n"; | |
1626 | } | |
1627 | ||
1628 | state.out << "</variablelist>\n"; | |
1629 | ||
1630 | values.finish(); | |
1631 | } | |
1632 | ||
1633 | void table_action(quickbook::state& state, value table) | |
1634 | { | |
1635 | write_anchors(state, state.out); | |
1636 | ||
1637 | value_consumer values = table; | |
1638 | ||
1639 | std::string element_id; | |
1640 | if(values.check(general_tags::element_id)) { | |
1641 | element_id = validate_id(state, values.consume()); | |
1642 | } | |
1643 | ||
1644 | value title = values.consume(table_tags::title); | |
1645 | bool has_title = !title.empty(); | |
1646 | ||
1647 | std::string table_id; | |
1648 | ||
1649 | if (!element_id.empty()) { | |
1650 | table_id = state.document.add_id(element_id, id_category::explicit_id); | |
1651 | } | |
1652 | else if (has_title) { | |
1653 | if (state.document.compatibility_version() >= 105) { | |
1654 | table_id = state.document.add_id(detail::make_identifier(title.get_quickbook()), id_category::generated); | |
1655 | } | |
1656 | else { | |
1657 | table_id = state.document.add_id("t", id_category::numbered); | |
1658 | } | |
1659 | } | |
1660 | ||
1661 | // Emulating the old behaviour which used the width of the final | |
1662 | // row for span_count. | |
1663 | int row_count = 0; | |
1664 | int span_count = 0; | |
1665 | ||
1666 | value_consumer lookahead = values; | |
1667 | BOOST_FOREACH(value row, lookahead) { | |
1668 | ++row_count; | |
1669 | span_count = boost::distance(row); | |
1670 | } | |
1671 | lookahead.finish(); | |
1672 | ||
1673 | if (has_title) | |
1674 | { | |
1675 | state.out << "<table frame=\"all\""; | |
1676 | if(!table_id.empty()) | |
1677 | state.out << " id=\"" << table_id << "\""; | |
1678 | state.out << ">\n"; | |
1679 | state.out << "<title>"; | |
1680 | if (qbk_version_n < 106u) { | |
1681 | detail::print_string(title.get_quickbook(), state.out.get()); | |
1682 | } | |
1683 | else { | |
1684 | state.out << title.get_encoded(); | |
1685 | } | |
1686 | state.out << "</title>"; | |
1687 | } | |
1688 | else | |
1689 | { | |
1690 | state.out << "<informaltable frame=\"all\""; | |
1691 | if(!table_id.empty()) | |
1692 | state.out << " id=\"" << table_id << "\""; | |
1693 | state.out << ">\n"; | |
1694 | } | |
1695 | ||
1696 | state.out << "<tgroup cols=\"" << span_count << "\">\n"; | |
1697 | ||
1698 | if (row_count > 1) | |
1699 | { | |
1700 | state.out << "<thead>" << "<row>"; | |
1701 | BOOST_FOREACH(value cell, values.consume()) { | |
1702 | state.out << "<entry>" << cell.get_encoded() << "</entry>"; | |
1703 | } | |
1704 | state.out << "</row>\n" << "</thead>\n"; | |
1705 | } | |
1706 | ||
1707 | state.out << "<tbody>\n"; | |
1708 | ||
1709 | BOOST_FOREACH(value row, values) { | |
1710 | state.out << "<row>"; | |
1711 | BOOST_FOREACH(value cell, row) { | |
1712 | state.out << "<entry>" << cell.get_encoded() << "</entry>"; | |
1713 | } | |
1714 | state.out << "</row>\n"; | |
1715 | } | |
1716 | ||
1717 | values.finish(); | |
1718 | ||
1719 | state.out << "</tbody>\n" | |
1720 | << "</tgroup>\n"; | |
1721 | ||
1722 | if (has_title) | |
1723 | { | |
1724 | state.out << "</table>\n"; | |
1725 | } | |
1726 | else | |
1727 | { | |
1728 | state.out << "</informaltable>\n"; | |
1729 | } | |
1730 | } | |
1731 | ||
1732 | void begin_section_action(quickbook::state& state, value begin_section_list) | |
1733 | { | |
1734 | value_consumer values = begin_section_list; | |
1735 | ||
1736 | value element_id = values.optional_consume(general_tags::element_id); | |
1737 | value content = values.consume(); | |
1738 | values.finish(); | |
1739 | ||
1740 | std::string full_id = state.document.begin_section( | |
b32b8144 | 1741 | element_id, |
7c673cae FG |
1742 | element_id.empty() ? |
1743 | detail::make_identifier(content.get_quickbook()) : | |
1744 | validate_id(state, element_id), | |
1745 | element_id.empty() ? | |
1746 | id_category::generated_section : | |
1747 | id_category::explicit_section_id, | |
1748 | state.current_source_mode()); | |
1749 | ||
1750 | state.out << "\n<section id=\"" << full_id << "\">\n"; | |
7c673cae | 1751 | |
b32b8144 | 1752 | std::string title = content.get_encoded(); |
7c673cae | 1753 | |
b32b8144 FG |
1754 | if (!title.empty()) { |
1755 | state.out << "<title>"; | |
1756 | ||
1757 | write_anchors(state, state.out); | |
1758 | ||
1759 | if (self_linked_headers && state.document.compatibility_version() >= 103) | |
1760 | { | |
1761 | state.out << quickbook::detail::linkify(title, full_id); | |
1762 | } | |
1763 | else | |
1764 | { | |
1765 | state.out << title; | |
1766 | } | |
1767 | ||
1768 | state.out << "</title>\n"; | |
7c673cae | 1769 | } |
7c673cae FG |
1770 | } |
1771 | ||
b32b8144 | 1772 | void end_section_action(quickbook::state& state, value end_section_list, string_iterator first) |
7c673cae | 1773 | { |
b32b8144 FG |
1774 | value_consumer values = end_section_list; |
1775 | value element_id = values.optional_consume(general_tags::element_id); | |
1776 | values.finish(); | |
1777 | ||
7c673cae FG |
1778 | write_anchors(state, state.out); |
1779 | ||
1780 | if (state.document.section_level() <= state.min_section_level) | |
1781 | { | |
1782 | file_position const pos = state.current_file->position_of(first); | |
1783 | ||
1784 | detail::outerr(state.current_file->path, pos.line) | |
1785 | << "Mismatched [endsect] near column " << pos.column << ".\n"; | |
1786 | ++state.error_count; | |
1787 | ||
1788 | return; | |
1789 | } | |
1790 | ||
b32b8144 FG |
1791 | if (!element_id.empty() && !(element_id == state.document.explicit_id())) |
1792 | { | |
1793 | file_position const pos = state.current_file->position_of(first); | |
1794 | value section_element_id = state.document.explicit_id(); | |
1795 | ||
1796 | if (section_element_id.empty()) { | |
1797 | detail::outerr(state.current_file->path, pos.line) | |
1798 | << "Endsect has unexpected id '" | |
1799 | << element_id.get_quickbook() | |
1800 | << "' in section with no explicit id, near column " | |
1801 | << pos.column << ".\n"; | |
1802 | } else { | |
1803 | detail::outerr(state.current_file->path, pos.line) | |
1804 | << "Endsect has incorrect id '" | |
1805 | << element_id.get_quickbook() | |
1806 | << "', expected '" | |
1807 | << state.document.explicit_id().get_quickbook() | |
1808 | << "', near column " | |
1809 | << pos.column << ".\n"; | |
1810 | } | |
1811 | ++state.error_count; | |
1812 | } | |
1813 | ||
7c673cae FG |
1814 | state.out << "</section>"; |
1815 | state.document.end_section(); | |
1816 | } | |
1817 | ||
1818 | void element_id_warning_action::operator()(parse_iterator first, parse_iterator) const | |
1819 | { | |
1820 | detail::outwarn(state.current_file, first.base()) << "Empty id.\n"; | |
1821 | } | |
1822 | ||
1823 | void xinclude_action(quickbook::state& state, value xinclude) | |
1824 | { | |
1825 | write_anchors(state, state.out); | |
1826 | ||
1827 | value_consumer values = xinclude; | |
1828 | path_parameter x = check_xinclude_path(values.consume(), state); | |
1829 | values.finish(); | |
1830 | ||
1831 | if (x.type == path_parameter::path) | |
1832 | { | |
b32b8144 | 1833 | quickbook_path path = resolve_xinclude_path(x.value, state, true); |
7c673cae FG |
1834 | |
1835 | state.out << "\n<xi:include href=\""; | |
1836 | detail::print_string(file_path_to_url(path.abstract_file_path), state.out.get()); | |
1837 | state.out << "\" />\n"; | |
1838 | } | |
1839 | } | |
1840 | ||
1841 | void load_quickbook(quickbook::state& state, | |
1842 | quickbook_path const& path, | |
1843 | value::tag_type load_type, | |
1844 | value const& include_doc_id = value()) | |
1845 | { | |
1846 | assert(load_type == block_tags::include || | |
1847 | load_type == block_tags::import); | |
1848 | ||
1849 | // Check this before qbk_version_n gets changed by the inner file. | |
1850 | bool keep_inner_source_mode = (qbk_version_n < 106); | |
1851 | ||
1852 | { | |
1853 | // When importing, state doesn't scope templates and macros so that | |
1854 | // they're added to the existing scope. It might be better to add | |
1855 | // them to a new scope then explicitly import them into the | |
1856 | // existing scope. | |
1857 | // | |
1858 | // For old versions of quickbook, templates aren't scoped by the | |
1859 | // file. | |
1860 | state_save save(state, | |
1861 | load_type == block_tags::import ? state_save::scope_output : | |
1862 | qbk_version_n >= 106u ? state_save::scope_callables : | |
1863 | state_save::scope_macros); | |
1864 | ||
1865 | state.current_file = load(path.file_path); // Throws load_error | |
1866 | state.current_path = path; | |
1867 | state.imported = (load_type == block_tags::import); | |
1868 | ||
1869 | // update the __FILENAME__ macro | |
1870 | state.update_filename_macro(); | |
1871 | ||
1872 | // parse the file | |
1873 | quickbook::parse_file(state, include_doc_id, true); | |
1874 | ||
1875 | // Don't restore source_mode on older versions. | |
1876 | if (keep_inner_source_mode) save.source_mode = state.source_mode; | |
1877 | } | |
1878 | ||
1879 | // restore the __FILENAME__ macro | |
1880 | state.update_filename_macro(); | |
1881 | } | |
1882 | ||
1883 | void load_source_file(quickbook::state& state, | |
1884 | quickbook_path const& path, | |
1885 | value::tag_type load_type, | |
1886 | string_iterator first, | |
b32b8144 | 1887 | value const& /* include_doc_id */ = value()) |
7c673cae FG |
1888 | { |
1889 | assert(load_type == block_tags::include || | |
1890 | load_type == block_tags::import); | |
1891 | ||
1892 | std::string ext = path.file_path.extension().generic_string(); | |
1893 | std::vector<template_symbol> storage; | |
1894 | // Throws load_error | |
1895 | state.error_count += | |
1896 | load_snippets(path.file_path, storage, ext, load_type); | |
1897 | ||
1898 | if (load_type == block_tags::include) | |
1899 | { | |
1900 | state.templates.push(); | |
1901 | } | |
1902 | ||
1903 | BOOST_FOREACH(template_symbol& ts, storage) | |
1904 | { | |
1905 | std::string tname = ts.identifier; | |
1906 | if (tname != "!") | |
1907 | { | |
1908 | ts.lexical_parent = &state.templates.top_scope(); | |
1909 | if (!state.templates.add(ts)) | |
1910 | { | |
1911 | detail::outerr(ts.content.get_file(), ts.content.get_position()) | |
1912 | << "Template Redefinition: " << tname << std::endl; | |
1913 | ++state.error_count; | |
1914 | } | |
1915 | } | |
1916 | } | |
1917 | ||
1918 | if (load_type == block_tags::include) | |
1919 | { | |
1920 | BOOST_FOREACH(template_symbol& ts, storage) | |
1921 | { | |
1922 | std::string tname = ts.identifier; | |
1923 | ||
1924 | if (tname == "!") | |
1925 | { | |
1926 | ts.lexical_parent = &state.templates.top_scope(); | |
1927 | call_code_snippet(state, &ts, first); | |
1928 | } | |
1929 | } | |
1930 | ||
1931 | state.templates.pop(); | |
1932 | } | |
1933 | } | |
1934 | ||
1935 | void include_action(quickbook::state& state, value include, string_iterator first) | |
1936 | { | |
1937 | write_anchors(state, state.out); | |
1938 | ||
1939 | value_consumer values = include; | |
1940 | value include_doc_id = values.optional_consume(general_tags::include_id); | |
1941 | path_parameter parameter = check_path(values.consume(), state); | |
1942 | values.finish(); | |
1943 | ||
1944 | std::set<quickbook_path> search = | |
1945 | include_search(parameter, state, first); | |
b32b8144 | 1946 | BOOST_FOREACH(quickbook_path const& path, search) |
7c673cae | 1947 | { |
7c673cae FG |
1948 | try { |
1949 | if (qbk_version_n >= 106) | |
1950 | { | |
1951 | if (state.imported && include.get_tag() == block_tags::include) | |
1952 | return; | |
1953 | ||
1954 | std::string ext = path.file_path.extension().generic_string(); | |
1955 | ||
1956 | if (ext == ".qbk" || ext == ".quickbook") | |
1957 | { | |
1958 | load_quickbook(state, path, include.get_tag(), include_doc_id); | |
1959 | } | |
1960 | else | |
1961 | { | |
1962 | load_source_file(state, path, include.get_tag(), first, include_doc_id); | |
1963 | } | |
1964 | } | |
1965 | else | |
1966 | { | |
1967 | if (include.get_tag() == block_tags::include) | |
1968 | { | |
1969 | load_quickbook(state, path, include.get_tag(), include_doc_id); | |
1970 | } | |
1971 | else | |
1972 | { | |
1973 | load_source_file(state, path, include.get_tag(), first, include_doc_id); | |
1974 | } | |
1975 | } | |
1976 | } | |
1977 | catch (load_error& e) { | |
1978 | ++state.error_count; | |
1979 | ||
1980 | detail::outerr(state.current_file, first) | |
1981 | << "Loading file " | |
1982 | << path.file_path | |
1983 | << ": " | |
1984 | << e.what() | |
1985 | << std::endl; | |
1986 | } | |
1987 | } | |
1988 | } | |
1989 | ||
1990 | bool to_value_scoped_action::start(value::tag_type t) | |
1991 | { | |
1992 | state.push_output(); | |
1993 | state.anchors.swap(saved_anchors); | |
1994 | tag = t; | |
1995 | ||
1996 | return true; | |
1997 | } | |
1998 | ||
1999 | void to_value_scoped_action::success(parse_iterator first, parse_iterator last) | |
2000 | { | |
2001 | std::string value; | |
2002 | ||
2003 | if (!state.out.str().empty()) | |
2004 | { | |
2005 | paragraph_action para(state); | |
2006 | para(); // For paragraphs before the template call. | |
2007 | write_anchors(state, state.out); | |
2008 | state.out.swap(value); | |
2009 | } | |
2010 | else | |
2011 | { | |
2012 | write_anchors(state, state.phrase); | |
2013 | state.phrase.swap(value); | |
2014 | } | |
2015 | ||
2016 | state.values.builder.insert(encoded_qbk_value( | |
2017 | state.current_file, first.base(), last.base(), value, tag)); | |
2018 | } | |
2019 | ||
2020 | ||
2021 | void to_value_scoped_action::cleanup() | |
2022 | { | |
2023 | state.pop_output(); | |
2024 | state.anchors.swap(saved_anchors); | |
2025 | } | |
2026 | } |