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