• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*=============================================================================
2     Copyright (c) 2002 2004 2006 Joel de Guzman
3     Copyright (c) 2004 Eric Niebler
4     http://spirit.sourceforge.net/
5 
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"
24 #include "files.hpp"
25 #include "for.hpp"
26 #include "grammar.hpp"
27 #include "path.hpp"
28 #include "post_process.hpp"
29 #include "state.hpp"
30 #include "stream.hpp"
31 #include "utils.hpp"
32 
33 #include <iterator>
34 #include <stdexcept>
35 #include <vector>
36 
37 #if defined(_WIN32)
38 #include <windows.h>
39 #include <shellapi.h>
40 #endif
41 
42 #if (defined(BOOST_MSVC) && (BOOST_MSVC <= 1310))
43 #pragma warning(disable : 4355)
44 #endif
45 
46 #define QUICKBOOK_VERSION "Quickbook Version 1.7.2"
47 
48 namespace quickbook
49 {
50     namespace cl = boost::spirit::classic;
51     namespace fs = boost::filesystem;
52 
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;
60 
set_macros(quickbook::state & state)61     static void set_macros(quickbook::state& state)
62     {
63         QUICKBOOK_FOR (quickbook::string_view val, preset_defines) {
64             parse_iterator first(val.begin());
65             parse_iterator last(val.end());
66 
67             cl::parse_info<parse_iterator> info =
68                 cl::parse(first, last, state.grammar().command_line_macro);
69 
70             if (!info.full) {
71                 detail::outerr() << "Error parsing command line definition: '"
72                                  << val << "'" << std::endl;
73                 ++state.error_count;
74             }
75         }
76     }
77 
78     ///////////////////////////////////////////////////////////////////////////
79     //
80     //  Parse a file
81     //
82     ///////////////////////////////////////////////////////////////////////////
parse_file(quickbook::state & state,value include_doc_id,bool nested_file)83     void parse_file(
84         quickbook::state& state, value include_doc_id, bool nested_file)
85     {
86         parse_iterator first(state.current_file->source().begin());
87         parse_iterator last(state.current_file->source().end());
88 
89         cl::parse_info<parse_iterator> info =
90             cl::parse(first, last, state.grammar().doc_info);
91         assert(info.hit);
92 
93         if (!state.error_count) {
94             std::string doc_type =
95                 pre(state, info.stop, include_doc_id, nested_file);
96 
97             info = cl::parse(
98                 info.hit ? info.stop : first, last,
99                 state.grammar().block_start);
100 
101             post(state, doc_type);
102 
103             if (!info.full) {
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";
108                 ++state.error_count;
109             }
110         }
111     }
112 
113     struct parse_document_options
114     {
115         enum output_format
116         {
117             boostbook,
118             html
119         };
120         enum output_style
121         {
122             output_none = 0,
123             output_file,
124             output_chunked
125         };
126 
parse_document_optionsquickbook::parse_document_options127         parse_document_options()
128             : format(boostbook)
129             , style(output_file)
130             , output_path()
131             , indent(-1)
132             , linewidth(-1)
133             , pretty_print(true)
134             , strict_mode(false)
135             , deps_out_flags(quickbook::dependency_tracker::default_)
136         {
137         }
138 
139         output_format format;
140         output_style style;
141         fs::path output_path;
142         int indent;
143         int linewidth;
144         bool pretty_print;
145         bool strict_mode;
146         fs::path deps_out;
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;
151     };
152 
parse_document(fs::path const & filein_,parse_document_options const & options_)153     static int parse_document(
154         fs::path const& filein_, parse_document_options const& options_)
155     {
156         string_stream buffer;
157         document_state output;
158 
159         int result = 0;
160 
161         try {
162             quickbook::state state(
163                 filein_, options_.xinclude_base, buffer, output);
164             state.strict_mode = options_.strict_mode;
165             set_macros(state);
166 
167             if (state.error_count == 0) {
168                 state.dependencies.add_dependency(filein_);
169                 state.current_file = load(filein_); // Throws load_error
170 
171                 parse_file(state);
172 
173                 if (state.error_count) {
174                     detail::outerr()
175                         << "Error count: " << state.error_count << ".\n";
176                 }
177             }
178 
179             result = state.error_count ? 1 : 0;
180 
181             if (!options_.deps_out.empty()) {
182                 state.dependencies.write_dependencies(
183                     options_.deps_out, options_.deps_out_flags);
184             }
185 
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);
190             }
191         } catch (load_error& e) {
192             detail::outerr(filein_) << e.what() << std::endl;
193             result = 1;
194         } catch (std::runtime_error& e) {
195             detail::outerr() << e.what() << std::endl;
196             result = 1;
197         }
198 
199         if (result) {
200             return result;
201         }
202 
203         if (options_.style) {
204             std::string stage2 = output.replace_placeholders(buffer.str());
205 
206             if (options_.pretty_print) {
207                 try {
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
215                         // error code.
216                         result = 1;
217                     }
218                     else {
219                         return 1;
220                     }
221                 }
222             }
223 
224             if (options_.format == parse_document_options::html) {
225                 if (result) {
226                     return result;
227                 }
228                 return quickbook::detail::boostbook_to_html(
229                     stage2, options_.html_ops);
230             }
231             else {
232                 fs::ofstream fileout(options_.output_path);
233 
234                 if (fileout.fail()) {
235                     ::quickbook::detail::outerr()
236                         << "Error opening output file " << options_.output_path
237                         << std::endl;
238 
239                     return 1;
240                 }
241 
242                 fileout << stage2;
243 
244                 if (fileout.fail()) {
245                     ::quickbook::detail::outerr()
246                         << "Error writing to output file "
247                         << options_.output_path << std::endl;
248 
249                     return 1;
250                 }
251             }
252         }
253 
254         return result;
255     }
256 }
257 
258 ///////////////////////////////////////////////////////////////////////////
259 //
260 //  Main program
261 //
262 ///////////////////////////////////////////////////////////////////////////
main(int argc,char * argv[])263 int main(int argc, char* argv[])
264 {
265     try {
266         namespace fs = boost::filesystem;
267         namespace po = boost::program_options;
268 
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;
277 
278         using namespace quickbook;
279         using quickbook::detail::command_line_string;
280 
281         // First thing, the filesystem should record the current working
282         // directory.
283         fs::initial_path<fs::path>();
284 
285         // Various initialisation methods
286         quickbook::detail::initialise_output();
287         quickbook::detail::initialise_markups();
288 
289         // Declare the program options
290 
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");
295 
296 #if QUICKBOOK_WIDE_PATHS
297 #define PO_VALUE po::wvalue
298 #else
299 #define PO_VALUE po::value
300 #endif
301 
302         // clang-format off
303 
304         desc.add_options()
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")
322         ;
323 
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)");
328         desc.add(html_desc);
329 
330         hidden.add_options()
331             ("debug", "debug mode")
332             ("expect-errors",
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 "
337                 "directory.")
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 '-' "
344              "if they weren't.\n"
345              "This is deprecated, use 'output-deps-format=checked' to "
346              "write the deps file in this format.")
347         ;
348 
349         // clang-format on
350 
351         all.add(desc).add(hidden);
352 
353         positional_options_description p;
354         p.add("input-file", -1);
355 
356         // Read option from the command line
357 
358         variables_map vm;
359 
360 #if QUICKBOOK_WIDE_PATHS
361         quickbook::ignore_variable(&argc);
362         quickbook::ignore_variable(&argv);
363 
364         int wide_argc;
365         LPWSTR* wide_argv = CommandLineToArgvW(GetCommandLineW(), &wide_argc);
366         if (!wide_argv) {
367             quickbook::detail::outerr()
368                 << "Error getting argument values." << std::endl;
369             return 1;
370         }
371 
372         store(
373             wcommand_line_parser(wide_argc, wide_argv)
374                 .options(all)
375                 .positional(p)
376                 .run(),
377             vm);
378 
379         LocalFree(wide_argv);
380 #else
381         store(
382             command_line_parser(argc, argv).options(all).positional(p).run(),
383             vm);
384 #endif
385 
386         notify(vm);
387 
388         // Process the command line options
389 
390         parse_document_options options;
391         bool expect_errors = vm.count("expect-errors");
392         int error_count = 0;
393         bool output_specified = false;
394         bool alt_output_specified = false;
395 
396         if (vm.count("help")) {
397             std::ostringstream description_text;
398             description_text << desc;
399 
400             quickbook::detail::out() << description_text.str() << "\n";
401 
402             return 0;
403         }
404 
405         if (vm.count("version")) {
406             std::string boost_version = BOOST_LIB_VERSION;
407             boost::replace(boost_version, '_', '.');
408 
409             quickbook::detail::out() << QUICKBOOK_VERSION << " (Boost "
410                                      << boost_version << ")" << std::endl;
411             return 0;
412         }
413 
414         quickbook::detail::set_ms_errors(vm.count("ms-errors"));
415 
416         if (vm.count("no-pretty-print")) options.pretty_print = false;
417 
418         options.strict_mode = !!vm.count("strict");
419 
420         if (vm.count("indent")) options.indent = vm["indent"].as<int>();
421 
422         if (vm.count("linewidth"))
423             options.linewidth = vm["linewidth"].as<int>();
424 
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;
431                 options.style =
432                     quickbook::parse_document_options::output_chunked;
433             }
434             else if (format == "onehtml") {
435                 options.format = quickbook::parse_document_options::html;
436                 options.style = quickbook::parse_document_options::output_file;
437             }
438             else if (format == "boostbook") {
439                 options.format = quickbook::parse_document_options::boostbook;
440                 options.style = quickbook::parse_document_options::output_file;
441             }
442             else {
443                 quickbook::detail::outerr()
444                     << "Unknown output format: " << format << std::endl;
445 
446                 ++error_count;
447             }
448         }
449 
450         quickbook::self_linked_headers =
451             options.format != parse_document_options::html &&
452             !vm.count("no-self-linked-headers");
453 
454         if (vm.count("debug")) {
455             static tm timeinfo;
456             timeinfo.tm_year = 2000 - 1900;
457             timeinfo.tm_mon = 12 - 1;
458             timeinfo.tm_mday = 20;
459             timeinfo.tm_hour = 12;
460             timeinfo.tm_min = 0;
461             timeinfo.tm_sec = 0;
462             timeinfo.tm_isdst = -1;
463             mktime(&timeinfo);
464             quickbook::current_time = &timeinfo;
465             quickbook::current_gm_time = &timeinfo;
466             quickbook::debug_mode = true;
467         }
468         else {
469             time_t t = std::time(0);
470             static tm lt = *localtime(&t);
471             static tm gmt = *gmtime(&t);
472             quickbook::current_time = &lt;
473             quickbook::current_gm_time = &gmt;
474             quickbook::debug_mode = false;
475         }
476 
477         quickbook::include_path.clear();
478         if (vm.count("include-path")) {
479             boost::transform(
480                 vm["include-path"].as<std::vector<command_line_string> >(),
481                 std::back_inserter(quickbook::include_path),
482                 quickbook::detail::command_line_to_path);
483         }
484 
485         quickbook::preset_defines.clear();
486         if (vm.count("define")) {
487             boost::transform(
488                 vm["define"].as<std::vector<command_line_string> >(),
489                 std::back_inserter(quickbook::preset_defines),
490                 quickbook::detail::command_line_to_utf8);
491         }
492 
493         if (vm.count("input-file")) {
494             fs::path filein = quickbook::detail::command_line_to_path(
495                 vm["input-file"].as<command_line_string>());
496 
497             if (!fs::exists(filein)) {
498                 quickbook::detail::outerr()
499                     << "file not found: " << filein << std::endl;
500                 ++error_count;
501             }
502 
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>());
507             }
508 
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>());
513 
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);
518 
519                 unsigned flags = 0;
520 
521                 QUICKBOOK_FOR (std::string const& flag, flag_names) {
522                     if (flag == "checked") {
523                         flags |= quickbook::dependency_tracker::checked;
524                     }
525                     else if (flag == "escaped") {
526                         flags |= quickbook::dependency_tracker::escaped;
527                     }
528                     else if (!flag.empty()) {
529                         quickbook::detail::outerr()
530                             << "Unknown dependency format flag: " << flag
531                             << std::endl;
532 
533                         ++error_count;
534                     }
535                 }
536 
537                 options.deps_out_flags =
538                     quickbook::dependency_tracker::flags(flags);
539             }
540 
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>());
545             }
546 
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>();
551             }
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.
555             //
556             // else {
557             //    options.html_ops.boost_root_path =
558             //      quickbook::detail::path_or_url::url(
559             //        "http://www.boost.org/doc/libs/release/");
560             //}
561 
562             if (vm.count("css-path")) {
563                 options.html_ops.css_path =
564                     vm["css-path"].as<command_line_string>();
565             }
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";
569             }
570 
571             if (vm.count("graphics-path")) {
572                 options.html_ops.graphics_path =
573                     vm["graphics-path"].as<command_line_string>();
574             }
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";
578             }
579 
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>());
587 
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"
592                             << std::endl;
593                         ++error_count;
594                     }
595                     break;
596                 }
597                 case quickbook::parse_document_options::output_chunked:
598                     quickbook::detail::outerr()
599                         << "output-file give for chunked output" << std::endl;
600                     ++error_count;
601                     break;
602                 case quickbook::parse_document_options::output_none:
603                     quickbook::detail::outerr()
604                         << "output-file given for no output" << std::endl;
605                     ++error_count;
606                     break;
607                 default:
608                     assert(false);
609                 }
610             }
611 
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>());
619 
620                     if (!fs::is_directory(options.output_path.parent_path())) {
621                         quickbook::detail::outerr()
622                             << "parent directory not found for output directory"
623                             << std::endl;
624                         ++error_count;
625                     }
626                 }
627                 case quickbook::parse_document_options::output_file:
628                     quickbook::detail::outerr()
629                         << "output-dir give for file output" << std::endl;
630                     ++error_count;
631                     break;
632                 case quickbook::parse_document_options::output_none:
633                     quickbook::detail::outerr()
634                         << "output-dir given for no output" << std::endl;
635                     ++error_count;
636                     break;
637                 default:
638                     assert(false);
639                 }
640             }
641 
642             if (!vm.count("output-file") && !vm.count("output-dir")) {
643                 if (!output_specified && alt_output_specified) {
644                     options.style =
645                         quickbook::parse_document_options::output_none;
646                 }
647                 else {
648                     fs::path path = filein;
649                     switch (options.style) {
650                     case quickbook::parse_document_options::output_chunked:
651                         path = path.parent_path() / "html";
652                         options.style =
653                             quickbook::parse_document_options::output_chunked;
654                         options.output_path = path;
655                         break;
656                     case quickbook::parse_document_options::output_file:
657                         switch (options.format) {
658                         case quickbook::parse_document_options::html:
659                             path.replace_extension(".html");
660                             break;
661                         case quickbook::parse_document_options::boostbook:
662                             path.replace_extension(".xml");
663                             break;
664                         default:
665                             assert(false);
666                             path.replace_extension(".xml");
667                         }
668                         options.output_path = path;
669                         break;
670                     default:
671                         assert(false);
672                         options.style =
673                             quickbook::parse_document_options::output_none;
674                     }
675                 }
676             }
677 
678             if (vm.count("xinclude-base")) {
679                 options.xinclude_base = quickbook::detail::command_line_to_path(
680                     vm["xinclude-base"].as<command_line_string>());
681 
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
685                 // paths.
686                 if (!fs::is_directory(options.xinclude_base)) {
687                     quickbook::detail::outerr()
688                         << "xinclude-base is not a directory" << std::endl;
689                     ++error_count;
690                 }
691             }
692             else {
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 = ".";
699                 }
700 
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
704                 // been checked.
705                 assert(error_count || fs::is_directory(options.xinclude_base));
706             }
707 
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>());
712             }
713             else {
714                 quickbook::image_location = filein.parent_path() / "html";
715             }
716 
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;
722             }
723             else {
724                 options.html_ops.home_path = options.output_path;
725                 options.html_ops.chunked_output = false;
726             }
727             options.html_ops.pretty_print = options.pretty_print;
728 
729             if (!error_count) {
730                 switch (options.style) {
731                 case parse_document_options::output_file:
732                     quickbook::detail::out()
733                         << "Generating output file: " << options.output_path
734                         << std::endl;
735                     break;
736                 case parse_document_options::output_chunked:
737                     quickbook::detail::out()
738                         << "Generating output path: " << options.output_path
739                         << std::endl;
740                     break;
741                 case parse_document_options::output_none:
742                     break;
743                 default:
744                     assert(false);
745                 }
746 
747                 error_count += quickbook::parse_document(filein, options);
748             }
749 
750             if (expect_errors) {
751                 if (!error_count)
752                     quickbook::detail::outerr()
753                         << "No errors detected for --expect-errors."
754                         << std::endl;
755                 return !error_count;
756             }
757             else {
758                 return error_count;
759             }
760         }
761         else {
762             std::ostringstream description_text;
763             description_text << desc;
764 
765             quickbook::detail::outerr() << "No filename given\n\n"
766                                         << description_text.str() << std::endl;
767             return 1;
768         }
769     }
770 
771     catch (std::exception& e) {
772         quickbook::detail::outerr() << e.what() << "\n";
773         return 1;
774     }
775 
776     catch (...) {
777         quickbook::detail::outerr() << "Exception of unknown type caught\n";
778         return 1;
779     }
780 
781     return 0;
782 }
783