1 /*=============================================================================
2 Copyright (c) 2002 2004 2006 Joel de Guzman
3 Copyright (c) 2004 Eric Niebler
4 http://spirit.sourceforge.net/
6 Use, modification and distribution is subject to the Boost Software
7 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
8 http://www.boost.org/LICENSE_1_0.txt)
9 =============================================================================*/
10 #include "quickbook.hpp"
11 #include <boost/algorithm/string/classification.hpp>
12 #include <boost/algorithm/string/split.hpp>
13 #include <boost/filesystem/fstream.hpp>
14 #include <boost/filesystem/operations.hpp>
15 #include <boost/filesystem/path.hpp>
16 #include <boost/program_options.hpp>
17 #include <boost/range/algorithm/replace.hpp>
18 #include <boost/range/algorithm/transform.hpp>
19 #include <boost/ref.hpp>
20 #include <boost/version.hpp>
21 #include "actions.hpp"
22 #include "bb2html.hpp"
23 #include "document_state.hpp"
26 #include "grammar.hpp"
28 #include "post_process.hpp"
42 #if (defined(BOOST_MSVC) && (BOOST_MSVC <= 1310))
43 #pragma warning(disable : 4355)
46 #define QUICKBOOK_VERSION "Quickbook Version 1.7.2"
50 namespace cl
= boost::spirit::classic
;
51 namespace fs
= boost::filesystem
;
53 tm
* current_time
; // the current time
54 tm
* current_gm_time
; // the current UTC time
55 bool debug_mode
; // for quickbook developers only
56 bool self_linked_headers
;
57 std::vector
<fs::path
> include_path
;
58 std::vector
<std::string
> preset_defines
;
59 fs::path image_location
;
61 static void set_macros(quickbook::state
& state
)
63 QUICKBOOK_FOR (quickbook::string_view val
, preset_defines
) {
64 parse_iterator
first(val
.begin());
65 parse_iterator
last(val
.end());
67 cl::parse_info
<parse_iterator
> info
=
68 cl::parse(first
, last
, state
.grammar().command_line_macro
);
71 detail::outerr() << "Error parsing command line definition: '"
72 << val
<< "'" << std::endl
;
78 ///////////////////////////////////////////////////////////////////////////
82 ///////////////////////////////////////////////////////////////////////////
84 quickbook::state
& state
, value include_doc_id
, bool nested_file
)
86 parse_iterator
first(state
.current_file
->source().begin());
87 parse_iterator
last(state
.current_file
->source().end());
89 cl::parse_info
<parse_iterator
> info
=
90 cl::parse(first
, last
, state
.grammar().doc_info
);
93 if (!state
.error_count
) {
94 std::string doc_type
=
95 pre(state
, info
.stop
, include_doc_id
, nested_file
);
98 info
.hit
? info
.stop
: first
, last
,
99 state
.grammar().block_start
);
101 post(state
, doc_type
);
104 file_position
const& pos
=
105 state
.current_file
->position_of(info
.stop
.base());
106 detail::outerr(state
.current_file
->path
, pos
.line
)
107 << "Syntax Error near column " << pos
.column
<< ".\n";
113 struct parse_document_options
127 parse_document_options()
135 , deps_out_flags(quickbook::dependency_tracker::default_
)
139 output_format format
;
141 fs::path output_path
;
147 quickbook::dependency_tracker::flags deps_out_flags
;
148 fs::path locations_out
;
149 fs::path xinclude_base
;
150 quickbook::detail::html_options html_ops
;
153 static int parse_document(
154 fs::path
const& filein_
, parse_document_options
const& options_
)
156 string_stream buffer
;
157 document_state output
;
162 quickbook::state
state(
163 filein_
, options_
.xinclude_base
, buffer
, output
);
164 state
.strict_mode
= options_
.strict_mode
;
167 if (state
.error_count
== 0) {
168 state
.dependencies
.add_dependency(filein_
);
169 state
.current_file
= load(filein_
); // Throws load_error
173 if (state
.error_count
) {
175 << "Error count: " << state
.error_count
<< ".\n";
179 result
= state
.error_count
? 1 : 0;
181 if (!options_
.deps_out
.empty()) {
182 state
.dependencies
.write_dependencies(
183 options_
.deps_out
, options_
.deps_out_flags
);
186 if (!options_
.locations_out
.empty()) {
187 fs::ofstream
out(options_
.locations_out
);
188 state
.dependencies
.write_dependencies(
189 options_
.locations_out
, dependency_tracker::checked
);
191 } catch (load_error
& e
) {
192 detail::outerr(filein_
) << e
.what() << std::endl
;
194 } catch (std::runtime_error
& e
) {
195 detail::outerr() << e
.what() << std::endl
;
203 if (options_
.style
) {
204 std::string stage2
= output
.replace_placeholders(buffer
.str());
206 if (options_
.pretty_print
) {
208 stage2
= post_process(
209 stage2
, options_
.indent
, options_
.linewidth
);
210 } catch (quickbook::post_process_failure
&) {
211 ::quickbook::detail::outerr()
212 << "Post Processing Failed." << std::endl
;
213 if (options_
.format
== parse_document_options::boostbook
) {
214 // Can still write out a boostbook file, but return an
224 if (options_
.format
== parse_document_options::html
) {
228 return quickbook::detail::boostbook_to_html(
229 stage2
, options_
.html_ops
);
232 fs::ofstream
fileout(options_
.output_path
);
234 if (fileout
.fail()) {
235 ::quickbook::detail::outerr()
236 << "Error opening output file " << options_
.output_path
244 if (fileout
.fail()) {
245 ::quickbook::detail::outerr()
246 << "Error writing to output file "
247 << options_
.output_path
<< std::endl
;
258 ///////////////////////////////////////////////////////////////////////////
262 ///////////////////////////////////////////////////////////////////////////
263 int main(int argc
, char* argv
[])
266 namespace fs
= boost::filesystem
;
267 namespace po
= boost::program_options
;
269 using boost::program_options::options_description
;
270 using boost::program_options::variables_map
;
271 using boost::program_options::store
;
272 using boost::program_options::parse_command_line
;
273 using boost::program_options::wcommand_line_parser
;
274 using boost::program_options::command_line_parser
;
275 using boost::program_options::notify
;
276 using boost::program_options::positional_options_description
;
278 using namespace quickbook
;
279 using quickbook::detail::command_line_string
;
281 // First thing, the filesystem should record the current working
283 fs::initial_path
<fs::path
>();
285 // Various initialisation methods
286 quickbook::detail::initialise_output();
287 quickbook::detail::initialise_markups();
289 // Declare the program options
291 options_description
desc("Allowed options");
292 options_description
html_desc("HTML options");
293 options_description
hidden("Hidden options");
294 options_description
all("All options");
296 #if QUICKBOOK_WIDE_PATHS
297 #define PO_VALUE po::wvalue
299 #define PO_VALUE po::value
305 ("help", "produce help message")
306 ("version", "print version string")
307 ("no-pretty-print", "disable XML pretty printing")
308 ("strict", "strict mode")
309 ("no-self-linked-headers", "stop headers linking to themselves")
310 ("indent", PO_VALUE
<int>(), "indent spaces")
311 ("linewidth", PO_VALUE
<int>(), "line width")
312 ("input-file", PO_VALUE
<command_line_string
>(), "input file")
313 ("output-format", PO_VALUE
<command_line_string
>(), "boostbook, html, onehtml")
314 ("output-file", PO_VALUE
<command_line_string
>(), "output file (for boostbook or onehtml)")
315 ("output-dir", PO_VALUE
<command_line_string
>(), "output directory (for html)")
316 ("no-output", "don't write out the result")
317 ("output-deps", PO_VALUE
<command_line_string
>(), "output dependency file")
318 ("ms-errors", "use Microsoft Visual Studio style error & warn message format")
319 ("include-path,I", PO_VALUE
< std::vector
<command_line_string
> >(), "include path")
320 ("define,D", PO_VALUE
< std::vector
<command_line_string
> >(), "define macro")
321 ("image-location", PO_VALUE
<command_line_string
>(), "image location")
324 html_desc
.add_options()
325 ("boost-root-path", PO_VALUE
<command_line_string
>(), "boost root (file path or absolute URL)")
326 ("css-path", PO_VALUE
<command_line_string
>(), "css file (file path or absolute URL)")
327 ("graphics-path", PO_VALUE
<command_line_string
>(), "graphics directory (file path or absolute URL)");
331 ("debug", "debug mode")
333 "Succeed if the input file contains a correctly handled "
334 "error, fail otherwise.")
335 ("xinclude-base", PO_VALUE
<command_line_string
>(),
336 "Generate xincludes as if generating for this target "
338 ("output-deps-format", PO_VALUE
<command_line_string
>(),
339 "Comma separated list of formatting options for output-deps, "
340 "options are: escaped, checked")
341 ("output-checked-locations", PO_VALUE
<command_line_string
>(),
342 "Writes a file listing all the file locations that were "
343 "checked, starting with '+' if they were found, or '-' "
345 "This is deprecated, use 'output-deps-format=checked' to "
346 "write the deps file in this format.")
351 all
.add(desc
).add(hidden
);
353 positional_options_description p
;
354 p
.add("input-file", -1);
356 // Read option from the command line
360 #if QUICKBOOK_WIDE_PATHS
361 quickbook::ignore_variable(&argc
);
362 quickbook::ignore_variable(&argv
);
365 LPWSTR
* wide_argv
= CommandLineToArgvW(GetCommandLineW(), &wide_argc
);
367 quickbook::detail::outerr()
368 << "Error getting argument values." << std::endl
;
373 wcommand_line_parser(wide_argc
, wide_argv
)
379 LocalFree(wide_argv
);
382 command_line_parser(argc
, argv
).options(all
).positional(p
).run(),
388 // Process the command line options
390 parse_document_options options
;
391 bool expect_errors
= vm
.count("expect-errors");
393 bool output_specified
= false;
394 bool alt_output_specified
= false;
396 if (vm
.count("help")) {
397 std::ostringstream description_text
;
398 description_text
<< desc
;
400 quickbook::detail::out() << description_text
.str() << "\n";
405 if (vm
.count("version")) {
406 std::string boost_version
= BOOST_LIB_VERSION
;
407 boost::replace(boost_version
, '_', '.');
409 quickbook::detail::out() << QUICKBOOK_VERSION
<< " (Boost "
410 << boost_version
<< ")" << std::endl
;
414 quickbook::detail::set_ms_errors(vm
.count("ms-errors"));
416 if (vm
.count("no-pretty-print")) options
.pretty_print
= false;
418 options
.strict_mode
= !!vm
.count("strict");
420 if (vm
.count("indent")) options
.indent
= vm
["indent"].as
<int>();
422 if (vm
.count("linewidth"))
423 options
.linewidth
= vm
["linewidth"].as
<int>();
425 if (vm
.count("output-format")) {
426 output_specified
= true;
427 std::string format
= quickbook::detail::command_line_to_utf8(
428 vm
["output-format"].as
<command_line_string
>());
429 if (format
== "html") {
430 options
.format
= quickbook::parse_document_options::html
;
432 quickbook::parse_document_options::output_chunked
;
434 else if (format
== "onehtml") {
435 options
.format
= quickbook::parse_document_options::html
;
436 options
.style
= quickbook::parse_document_options::output_file
;
438 else if (format
== "boostbook") {
439 options
.format
= quickbook::parse_document_options::boostbook
;
440 options
.style
= quickbook::parse_document_options::output_file
;
443 quickbook::detail::outerr()
444 << "Unknown output format: " << format
<< std::endl
;
450 quickbook::self_linked_headers
=
451 options
.format
!= parse_document_options::html
&&
452 !vm
.count("no-self-linked-headers");
454 if (vm
.count("debug")) {
456 timeinfo
.tm_year
= 2000 - 1900;
457 timeinfo
.tm_mon
= 12 - 1;
458 timeinfo
.tm_mday
= 20;
459 timeinfo
.tm_hour
= 12;
462 timeinfo
.tm_isdst
= -1;
464 quickbook::current_time
= &timeinfo
;
465 quickbook::current_gm_time
= &timeinfo
;
466 quickbook::debug_mode
= true;
469 time_t t
= std::time(0);
470 static tm lt
= *localtime(&t
);
471 static tm gmt
= *gmtime(&t
);
472 quickbook::current_time
= <
;
473 quickbook::current_gm_time
= &gmt
;
474 quickbook::debug_mode
= false;
477 quickbook::include_path
.clear();
478 if (vm
.count("include-path")) {
480 vm
["include-path"].as
<std::vector
<command_line_string
> >(),
481 std::back_inserter(quickbook::include_path
),
482 quickbook::detail::command_line_to_path
);
485 quickbook::preset_defines
.clear();
486 if (vm
.count("define")) {
488 vm
["define"].as
<std::vector
<command_line_string
> >(),
489 std::back_inserter(quickbook::preset_defines
),
490 quickbook::detail::command_line_to_utf8
);
493 if (vm
.count("input-file")) {
494 fs::path filein
= quickbook::detail::command_line_to_path(
495 vm
["input-file"].as
<command_line_string
>());
497 if (!fs::exists(filein
)) {
498 quickbook::detail::outerr()
499 << "file not found: " << filein
<< std::endl
;
503 if (vm
.count("output-deps")) {
504 alt_output_specified
= true;
505 options
.deps_out
= quickbook::detail::command_line_to_path(
506 vm
["output-deps"].as
<command_line_string
>());
509 if (vm
.count("output-deps-format")) {
510 std::string format_flags
=
511 quickbook::detail::command_line_to_utf8(
512 vm
["output-deps-format"].as
<command_line_string
>());
514 std::vector
<std::string
> flag_names
;
515 boost::algorithm::split(
516 flag_names
, format_flags
, boost::algorithm::is_any_of(", "),
517 boost::algorithm::token_compress_on
);
521 QUICKBOOK_FOR (std::string
const& flag
, flag_names
) {
522 if (flag
== "checked") {
523 flags
|= quickbook::dependency_tracker::checked
;
525 else if (flag
== "escaped") {
526 flags
|= quickbook::dependency_tracker::escaped
;
528 else if (!flag
.empty()) {
529 quickbook::detail::outerr()
530 << "Unknown dependency format flag: " << flag
537 options
.deps_out_flags
=
538 quickbook::dependency_tracker::flags(flags
);
541 if (vm
.count("output-checked-locations")) {
542 alt_output_specified
= true;
543 options
.locations_out
= quickbook::detail::command_line_to_path(
544 vm
["output-checked-locations"].as
<command_line_string
>());
547 if (vm
.count("boost-root-path")) {
548 // TODO: Check that it's a directory?
549 options
.html_ops
.boost_root_path
=
550 vm
["boost-root-path"].as
<command_line_string
>();
552 // Could possibly default it:
553 // 'boost:' links will use this anyway, but setting a default
554 // would also result in default css and graphics paths.
557 // options.html_ops.boost_root_path =
558 // quickbook::detail::path_or_url::url(
559 // "http://www.boost.org/doc/libs/release/");
562 if (vm
.count("css-path")) {
563 options
.html_ops
.css_path
=
564 vm
["css-path"].as
<command_line_string
>();
566 else if (options
.html_ops
.boost_root_path
) {
567 options
.html_ops
.css_path
=
568 options
.html_ops
.boost_root_path
/ "doc/src/boostbook.css";
571 if (vm
.count("graphics-path")) {
572 options
.html_ops
.graphics_path
=
573 vm
["graphics-path"].as
<command_line_string
>();
575 else if (options
.html_ops
.boost_root_path
) {
576 options
.html_ops
.graphics_path
=
577 options
.html_ops
.boost_root_path
/ "doc/src/images";
580 if (vm
.count("output-file")) {
581 output_specified
= true;
582 switch (options
.style
) {
583 case quickbook::parse_document_options::output_file
: {
584 options
.output_path
=
585 quickbook::detail::command_line_to_path(
586 vm
["output-file"].as
<command_line_string
>());
588 fs::path parent
= options
.output_path
.parent_path();
589 if (!parent
.empty() && !fs::is_directory(parent
)) {
590 quickbook::detail::outerr()
591 << "parent directory not found for output file"
597 case quickbook::parse_document_options::output_chunked
:
598 quickbook::detail::outerr()
599 << "output-file give for chunked output" << std::endl
;
602 case quickbook::parse_document_options::output_none
:
603 quickbook::detail::outerr()
604 << "output-file given for no output" << std::endl
;
612 if (vm
.count("output-dir")) {
613 output_specified
= true;
614 switch (options
.style
) {
615 case quickbook::parse_document_options::output_chunked
: {
616 options
.output_path
=
617 quickbook::detail::command_line_to_path(
618 vm
["output-dir"].as
<command_line_string
>());
620 if (!fs::is_directory(options
.output_path
.parent_path())) {
621 quickbook::detail::outerr()
622 << "parent directory not found for output directory"
627 case quickbook::parse_document_options::output_file
:
628 quickbook::detail::outerr()
629 << "output-dir give for file output" << std::endl
;
632 case quickbook::parse_document_options::output_none
:
633 quickbook::detail::outerr()
634 << "output-dir given for no output" << std::endl
;
642 if (!vm
.count("output-file") && !vm
.count("output-dir")) {
643 if (!output_specified
&& alt_output_specified
) {
645 quickbook::parse_document_options::output_none
;
648 fs::path path
= filein
;
649 switch (options
.style
) {
650 case quickbook::parse_document_options::output_chunked
:
651 path
= path
.parent_path() / "html";
653 quickbook::parse_document_options::output_chunked
;
654 options
.output_path
= path
;
656 case quickbook::parse_document_options::output_file
:
657 switch (options
.format
) {
658 case quickbook::parse_document_options::html
:
659 path
.replace_extension(".html");
661 case quickbook::parse_document_options::boostbook
:
662 path
.replace_extension(".xml");
666 path
.replace_extension(".xml");
668 options
.output_path
= path
;
673 quickbook::parse_document_options::output_none
;
678 if (vm
.count("xinclude-base")) {
679 options
.xinclude_base
= quickbook::detail::command_line_to_path(
680 vm
["xinclude-base"].as
<command_line_string
>());
682 // I'm not sure if this error check is necessary.
683 // There might be valid reasons to use a path that doesn't
684 // exist yet, or a path that just generates valid relative
686 if (!fs::is_directory(options
.xinclude_base
)) {
687 quickbook::detail::outerr()
688 << "xinclude-base is not a directory" << std::endl
;
693 options
.xinclude_base
=
694 options
.style
== parse_document_options::output_chunked
695 ? options
.output_path
696 : options
.output_path
.parent_path();
697 if (options
.xinclude_base
.empty()) {
698 options
.xinclude_base
= ".";
701 // If output_path was implicitly created from filein, then it
702 // should be in filein's directory.
703 // If output_path was explicitly specified, then it's already
705 assert(error_count
|| fs::is_directory(options
.xinclude_base
));
708 if (vm
.count("image-location")) {
709 quickbook::image_location
=
710 quickbook::detail::command_line_to_path(
711 vm
["image-location"].as
<command_line_string
>());
714 quickbook::image_location
= filein
.parent_path() / "html";
717 // Set duplicated html_options.
718 // TODO: Clean this up?
719 if (options
.style
== parse_document_options::output_chunked
) {
720 options
.html_ops
.home_path
= options
.output_path
/ "index.html";
721 options
.html_ops
.chunked_output
= true;
724 options
.html_ops
.home_path
= options
.output_path
;
725 options
.html_ops
.chunked_output
= false;
727 options
.html_ops
.pretty_print
= options
.pretty_print
;
730 switch (options
.style
) {
731 case parse_document_options::output_file
:
732 quickbook::detail::out()
733 << "Generating output file: " << options
.output_path
736 case parse_document_options::output_chunked
:
737 quickbook::detail::out()
738 << "Generating output path: " << options
.output_path
741 case parse_document_options::output_none
:
747 error_count
+= quickbook::parse_document(filein
, options
);
752 quickbook::detail::outerr()
753 << "No errors detected for --expect-errors."
762 std::ostringstream description_text
;
763 description_text
<< desc
;
765 quickbook::detail::outerr() << "No filename given\n\n"
766 << description_text
.str() << std::endl
;
771 catch (std::exception
& e
) {
772 quickbook::detail::outerr() << e
.what() << "\n";
777 quickbook::detail::outerr() << "Exception of unknown type caught\n";