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 "grammar.hpp"
11 #include "quickbook.hpp"
13 #include "actions.hpp"
14 #include "post_process.hpp"
19 #include "document_state.hpp"
20 #include <boost/program_options.hpp>
21 #include <boost/filesystem/path.hpp>
22 #include <boost/filesystem/operations.hpp>
23 #include <boost/filesystem/fstream.hpp>
24 #include <boost/range/algorithm.hpp>
25 #include <boost/ref.hpp>
26 #include <boost/version.hpp>
27 #include <boost/foreach.hpp>
28 #include <boost/algorithm/string/split.hpp>
29 #include <boost/algorithm/string/classification.hpp>
40 #if (defined(BOOST_MSVC) && (BOOST_MSVC <= 1310))
41 #pragma warning(disable:4355)
44 #define QUICKBOOK_VERSION "Quickbook Version 1.7.0"
48 namespace cl
= boost::spirit::classic
;
49 namespace fs
= boost::filesystem
;
51 tm
* current_time
; // the current time
52 tm
* current_gm_time
; // the current UTC time
53 bool debug_mode
; // for quickbook developers only
54 bool self_linked_headers
;
55 std::vector
<fs::path
> include_path
;
56 std::vector
<std::string
> preset_defines
;
57 fs::path image_location
;
59 static void set_macros(quickbook::state
& state
)
61 for(std::vector
<std::string
>::const_iterator
62 it
= preset_defines
.begin(),
63 end
= preset_defines
.end();
66 quickbook::string_view
val(*it
);
67 parse_iterator
first(val
.begin());
68 parse_iterator
last(val
.end());
70 cl::parse_info
<parse_iterator
> info
=
71 cl::parse(first
, last
, state
.grammar().command_line_macro
);
75 << "Error parsing command line definition: '"
84 ///////////////////////////////////////////////////////////////////////////
88 ///////////////////////////////////////////////////////////////////////////
89 void parse_file(quickbook::state
& state
, value include_doc_id
, bool nested_file
)
91 parse_iterator
first(state
.current_file
->source().begin());
92 parse_iterator
last(state
.current_file
->source().end());
94 cl::parse_info
<parse_iterator
> info
= cl::parse(first
, last
, state
.grammar().doc_info
);
97 if (!state
.error_count
)
99 std::string doc_type
= pre(state
, info
.stop
, include_doc_id
, nested_file
);
101 info
= cl::parse(info
.hit
? info
.stop
: first
, last
, state
.grammar().block_start
);
103 post(state
, doc_type
);
107 file_position
const& pos
= state
.current_file
->position_of(info
.stop
.base());
108 detail::outerr(state
.current_file
->path
, pos
.line
)
109 << "Syntax Error near column " << pos
.column
<< ".\n";
115 struct parse_document_options
117 parse_document_options() :
122 deps_out_flags(quickbook::dependency_tracker::default_
)
130 quickbook::dependency_tracker::flags deps_out_flags
;
131 fs::path locations_out
;
132 fs::path xinclude_base
;
137 fs::path
const& filein_
138 , fs::path
const& fileout_
139 , parse_document_options
const& options_
)
141 string_stream buffer
;
142 document_state output
;
147 quickbook::state
state(filein_
, options_
.xinclude_base
, buffer
, output
);
148 state
.strict_mode
= options_
.strict_mode
;
151 if (state
.error_count
== 0) {
152 state
.dependencies
.add_dependency(filein_
);
153 state
.current_file
= load(filein_
); // Throws load_error
157 if(state
.error_count
) {
159 << "Error count: " << state
.error_count
<< ".\n";
163 result
= state
.error_count
? 1 : 0;
165 if (!options_
.deps_out
.empty())
167 state
.dependencies
.write_dependencies(options_
.deps_out
,
168 options_
.deps_out_flags
);
171 if (!options_
.locations_out
.empty())
173 fs::ofstream
out(options_
.locations_out
);
174 state
.dependencies
.write_dependencies(options_
.locations_out
,
175 dependency_tracker::checked
);
178 catch (load_error
& e
) {
179 detail::outerr(filein_
) << e
.what() << std::endl
;
182 catch (std::runtime_error
& e
) {
183 detail::outerr() << e
.what() << std::endl
;
187 if (!fileout_
.empty() && result
== 0)
189 std::string stage2
= output
.replace_placeholders(buffer
.str());
191 fs::ofstream
fileout(fileout_
);
193 if (fileout
.fail()) {
194 ::quickbook::detail::outerr()
195 << "Error opening output file "
202 if (options_
.pretty_print
)
206 fileout
<< post_process(stage2
, options_
.indent
,
209 catch (quickbook::post_process_failure
&)
212 ::quickbook::detail::outerr()
213 << "Post Processing Failed."
224 if (fileout
.fail()) {
225 ::quickbook::detail::outerr()
226 << "Error writing to output file "
238 ///////////////////////////////////////////////////////////////////////////
242 ///////////////////////////////////////////////////////////////////////////
244 main(int argc
, char* argv
[])
248 namespace fs
= boost::filesystem
;
249 namespace po
= boost::program_options
;
251 using boost::program_options::options_description
;
252 using boost::program_options::variables_map
;
253 using boost::program_options::store
;
254 using boost::program_options::parse_command_line
;
255 using boost::program_options::wcommand_line_parser
;
256 using boost::program_options::command_line_parser
;
257 using boost::program_options::notify
;
258 using boost::program_options::positional_options_description
;
260 using namespace quickbook
;
261 using quickbook::detail::command_line_string
;
263 // First thing, the filesystem should record the current working directory.
264 fs::initial_path
<fs::path
>();
266 // Various initialisation methods
267 quickbook::detail::initialise_output();
268 quickbook::detail::initialise_markups();
270 // Declare the program options
272 options_description
desc("Allowed options");
273 options_description
hidden("Hidden options");
274 options_description
all("All options");
276 #if QUICKBOOK_WIDE_PATHS
277 #define PO_VALUE po::wvalue
279 #define PO_VALUE po::value
283 ("help", "produce help message")
284 ("version", "print version string")
285 ("no-pretty-print", "disable XML pretty printing")
286 ("strict", "strict mode")
287 ("no-self-linked-headers", "stop headers linking to themselves")
288 ("indent", PO_VALUE
<int>(), "indent spaces")
289 ("linewidth", PO_VALUE
<int>(), "line width")
290 ("input-file", PO_VALUE
<command_line_string
>(), "input file")
291 ("output-file", PO_VALUE
<command_line_string
>(), "output file")
292 ("no-output", "don't write out the result (overriden by --output-file)")
293 ("output-deps", PO_VALUE
<command_line_string
>(), "output dependency file")
294 ("ms-errors", "use Microsoft Visual Studio style error & warn message format")
295 ("include-path,I", PO_VALUE
< std::vector
<command_line_string
> >(), "include path")
296 ("define,D", PO_VALUE
< std::vector
<command_line_string
> >(), "define macro")
297 ("image-location", PO_VALUE
<command_line_string
>(), "image location")
301 ("debug", "debug mode")
303 "Succeed if the input file contains a correctly handled "
304 "error, fail otherwise.")
305 ("xinclude-base", PO_VALUE
<command_line_string
>(),
306 "Generate xincludes as if generating for this target "
308 ("output-deps-format", PO_VALUE
<command_line_string
>(),
309 "Comma separated list of formatting options for output-deps, "
310 "options are: escaped, checked")
311 ("output-checked-locations", PO_VALUE
<command_line_string
>(),
312 "Writes a file listing all the file locations that were "
313 "checked, starting with '+' if they were found, or '-' "
315 "This is deprecated, use 'output-deps-format=checked' to "
316 "write the deps file in this format.")
319 all
.add(desc
).add(hidden
);
321 positional_options_description p
;
322 p
.add("input-file", -1);
324 // Read option from the command line
328 #if QUICKBOOK_WIDE_PATHS
329 quickbook::ignore_variable(&argc
);
330 quickbook::ignore_variable(&argv
);
333 LPWSTR
* wide_argv
= CommandLineToArgvW(GetCommandLineW(), &wide_argc
);
336 quickbook::detail::outerr() << "Error getting argument values." << std::endl
;
341 wcommand_line_parser(wide_argc
, wide_argv
)
346 LocalFree(wide_argv
);
348 store(command_line_parser(argc
, argv
)
356 // Process the command line options
358 parse_document_options options
;
359 bool expect_errors
= vm
.count("expect-errors");
362 if (vm
.count("help"))
364 std::ostringstream description_text
;
365 description_text
<< desc
;
367 quickbook::detail::out() << description_text
.str() << "\n";
372 if (vm
.count("version"))
374 std::string boost_version
= BOOST_LIB_VERSION
;
375 boost::replace(boost_version
, '_', '.');
377 quickbook::detail::out()
386 quickbook::detail::set_ms_errors(vm
.count("ms-errors"));
388 if (vm
.count("no-pretty-print"))
389 options
.pretty_print
= false;
391 options
.strict_mode
= !!vm
.count("strict");
393 quickbook::self_linked_headers
= !vm
.count("no-self-linked-headers");
395 if (vm
.count("indent"))
396 options
.indent
= vm
["indent"].as
<int>();
398 if (vm
.count("linewidth"))
399 options
.linewidth
= vm
["linewidth"].as
<int>();
401 if (vm
.count("debug"))
404 timeinfo
.tm_year
= 2000 - 1900;
405 timeinfo
.tm_mon
= 12 - 1;
406 timeinfo
.tm_mday
= 20;
407 timeinfo
.tm_hour
= 12;
410 timeinfo
.tm_isdst
= -1;
412 quickbook::current_time
= &timeinfo
;
413 quickbook::current_gm_time
= &timeinfo
;
414 quickbook::debug_mode
= true;
418 time_t t
= std::time(0);
419 static tm lt
= *localtime(&t
);
420 static tm gmt
= *gmtime(&t
);
421 quickbook::current_time
= <
;
422 quickbook::current_gm_time
= &gmt
;
423 quickbook::debug_mode
= false;
426 quickbook::include_path
.clear();
427 if (vm
.count("include-path"))
430 vm
["include-path"].as
<std::vector
<command_line_string
> >(),
431 std::back_inserter(quickbook::include_path
),
432 quickbook::detail::command_line_to_path
);
435 quickbook::preset_defines
.clear();
436 if (vm
.count("define"))
439 vm
["define"].as
<std::vector
<command_line_string
> >(),
440 std::back_inserter(quickbook::preset_defines
),
441 quickbook::detail::command_line_to_utf8
);
444 if (vm
.count("input-file"))
446 fs::path filein
= quickbook::detail::command_line_to_path(
447 vm
["input-file"].as
<command_line_string
>());
450 if (!fs::exists(filein
)) {
451 quickbook::detail::outerr()
452 << "file not found: "
458 bool default_output
= true;
460 if (vm
.count("no-output"))
462 default_output
= false;
465 if (vm
.count("output-deps"))
468 quickbook::detail::command_line_to_path(
469 vm
["output-deps"].as
<command_line_string
>());
470 default_output
= false;
473 if (vm
.count("output-deps-format"))
475 std::string format_flags
=
476 quickbook::detail::command_line_to_utf8(
477 vm
["output-deps-format"].as
<command_line_string
>());
479 std::vector
<std::string
> flag_names
;
480 boost::algorithm::split(flag_names
, format_flags
,
481 boost::algorithm::is_any_of(", "),
482 boost::algorithm::token_compress_on
);
486 BOOST_FOREACH(std::string
const& flag
, flag_names
) {
487 if (flag
== "checked") {
488 flags
|= quickbook::dependency_tracker::checked
;
490 else if (flag
== "escaped") {
491 flags
|= quickbook::dependency_tracker::escaped
;
493 else if (!flag
.empty()) {
494 quickbook::detail::outerr()
495 << "Unknown dependency format flag: "
503 options
.deps_out_flags
=
504 quickbook::dependency_tracker::flags(flags
);
507 if (vm
.count("output-checked-locations"))
509 options
.locations_out
=
510 quickbook::detail::command_line_to_path(
511 vm
["output-checked-locations"].as
<command_line_string
>());
512 default_output
= false;
515 if (vm
.count("output-file"))
517 fileout
= quickbook::detail::command_line_to_path(
518 vm
["output-file"].as
<command_line_string
>());
520 fs::path parent
= fileout
.parent_path();
521 if (!parent
.empty() && !fs::is_directory(parent
))
523 quickbook::detail::outerr()
524 << "parent directory not found for output file"
529 else if (default_output
)
532 fileout
.replace_extension(".xml");
535 if (vm
.count("xinclude-base"))
537 options
.xinclude_base
=
538 quickbook::detail::command_line_to_path(
539 vm
["xinclude-base"].as
<command_line_string
>());
541 // I'm not sure if this error check is necessary.
542 // There might be valid reasons to use a path that doesn't
543 // exist yet, or a path that just generates valid relative
545 if (!fs::is_directory(options
.xinclude_base
))
547 quickbook::detail::outerr()
548 << "xinclude-base is not a directory"
555 options
.xinclude_base
= fileout
.parent_path();
556 if (options
.xinclude_base
.empty()) {
557 options
.xinclude_base
= ".";
560 // If fileout was implicitly created from filein, then it should be in filein's directory.
561 // If fileout was explicitly specified, then it's already been checked.
562 assert(error_count
|| fs::is_directory(options
.xinclude_base
));
565 if (vm
.count("image-location"))
567 quickbook::image_location
= quickbook::detail::command_line_to_path(
568 vm
["image-location"].as
<command_line_string
>());
572 quickbook::image_location
= filein
.parent_path() / "html";
576 if (!fileout
.empty()) {
577 quickbook::detail::out() << "Generating Output File: "
582 error_count
+= quickbook::parse_document(
583 filein
, fileout
, options
);
588 if (!error_count
) quickbook::detail::outerr() << "No errors detected for --expect-errors." << std::endl
;
598 std::ostringstream description_text
;
599 description_text
<< desc
;
601 quickbook::detail::outerr() << "No filename given\n\n"
602 << description_text
.str() << std::endl
;
607 catch(std::exception
& e
)
609 quickbook::detail::outerr() << e
.what() << "\n";
615 quickbook::detail::outerr() << "Exception of unknown type caught\n";