summaryrefslogtreecommitdiffstats
path: root/src/boost/tools/quickbook/src/doc_info_actions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/tools/quickbook/src/doc_info_actions.cpp')
-rw-r--r--src/boost/tools/quickbook/src/doc_info_actions.cpp608
1 files changed, 608 insertions, 0 deletions
diff --git a/src/boost/tools/quickbook/src/doc_info_actions.cpp b/src/boost/tools/quickbook/src/doc_info_actions.cpp
new file mode 100644
index 000000000..d3c9ac4bc
--- /dev/null
+++ b/src/boost/tools/quickbook/src/doc_info_actions.cpp
@@ -0,0 +1,608 @@
+/*=============================================================================
+ Copyright (c) 2002 2004 2006 Joel de Guzman
+ Copyright (c) 2004 Eric Niebler
+ Copyright (c) 2005 Thomas Guest
+ http://spirit.sourceforge.net/
+
+ Use, modification and distribution is subject to the Boost Software
+ License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================*/
+#include <sstream>
+#include <boost/algorithm/string/join.hpp>
+#include <boost/bind.hpp>
+#include <boost/filesystem/operations.hpp>
+#include "doc_info_tags.hpp"
+#include "document_state.hpp"
+#include "files.hpp"
+#include "for.hpp"
+#include "path.hpp"
+#include "quickbook.hpp"
+#include "state.hpp"
+#include "stream.hpp"
+#include "utils.hpp"
+
+namespace quickbook
+{
+ struct doc_info_values
+ {
+ std::string doc_type;
+ value doc_title;
+ std::vector<value> escaped_attributes;
+ value qbk_version, compatibility_mode;
+ value id, dirname, last_revision, purpose;
+ std::vector<value> categories;
+ value lang, version;
+ std::vector<value> authors;
+ std::vector<value> copyrights;
+ value license;
+ std::vector<value> biblioids;
+ value xmlbase;
+ std::string xmlbase_value;
+
+ std::string id_placeholder;
+ std::string include_doc_id_, id_;
+ };
+
+ static void write_document_title(
+ collector& out, value const& title, value const& version);
+ std::string write_boostbook_header(
+ quickbook::state& state, doc_info_values const& info, bool nested_file);
+
+ static std::string doc_info_output(value const& p, unsigned version)
+ {
+ if (qbk_version_n < version) {
+ std::string value = p.get_quickbook().to_s();
+ value.erase(value.find_last_not_of(" \t") + 1);
+ return value;
+ }
+ else {
+ return p.get_encoded();
+ }
+ }
+
+ char const* doc_info_attribute_name(value::tag_type tag)
+ {
+ return doc_attributes::is_tag(tag) ? doc_attributes::name(tag)
+ : doc_info_attributes::name(tag);
+ }
+
+ // Each docinfo attribute is stored in a value list, these are then stored
+ // in a sorted value list. The following convenience methods extract all the
+ // values for an attribute tag.
+
+ // Expecting at most one attribute, with several values in the list.
+ value consume_list(
+ value_consumer& c,
+ value::tag_type tag,
+ std::vector<std::string>* duplicates)
+ {
+ value p;
+
+ int count = 0;
+ while (c.check(tag)) {
+ p = c.consume();
+ ++count;
+ }
+
+ if (count > 1) duplicates->push_back(doc_info_attribute_name(tag));
+
+ return p;
+ }
+
+ // Expecting at most one attribute, with a single value, so extract that
+ // immediately.
+ value consume_value_in_list(
+ value_consumer& c,
+ value::tag_type tag,
+ std::vector<std::string>* duplicates)
+ {
+ value l = consume_list(c, tag, duplicates);
+ if (l.empty()) return l;
+
+ assert(l.is_list());
+ value_consumer c2 = l;
+ value p = c2.consume();
+ c2.finish();
+
+ return p;
+ }
+
+ // Any number of attributes, so stuff them into a vector.
+ std::vector<value> consume_multiple_values(
+ value_consumer& c, value::tag_type tag)
+ {
+ std::vector<value> values;
+
+ while (c.check(tag)) {
+ values.push_back(c.consume());
+ }
+
+ return values;
+ }
+
+ enum version_state
+ {
+ version_unknown,
+ version_stable,
+ version_dev
+ };
+ version_state classify_version(unsigned v)
+ {
+ return v < 100u ? version_unknown
+ : v <= 107u ? version_stable :
+ // v <= 107u ? version_dev :
+ version_unknown;
+ }
+
+ unsigned get_version(
+ quickbook::state& state, bool using_docinfo, value version)
+ {
+ unsigned result = 0;
+
+ if (!version.empty()) {
+ value_consumer version_values(version);
+ bool before_docinfo =
+ version_values.optional_consume(doc_info_tags::before_docinfo)
+ .check();
+ int major_verison = version_values.consume().get_int();
+ int minor_verison = version_values.consume().get_int();
+ version_values.finish();
+
+ if (before_docinfo || using_docinfo) {
+ result =
+ ((unsigned)major_verison * 100) + (unsigned)minor_verison;
+
+ if (classify_version(result) == version_unknown) {
+ detail::outerr(state.current_file->path)
+ << "Unknown version: " << major_verison << "."
+ << minor_verison << std::endl;
+ ++state.error_count;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ std::string pre(
+ quickbook::state& state,
+ parse_iterator pos,
+ value include_doc_id,
+ bool nested_file)
+ {
+ // The doc_info in the file has been parsed. Here's what we'll do
+ // *before* anything else.
+ //
+ // If there isn't a doc info block, then values will be empty, so most
+ // of the following code won't actually do anything.
+
+ value_consumer values = state.values.release();
+
+ // Skip over invalid attributes
+
+ while (values.check(value::default_tag))
+ values.consume();
+
+ bool use_doc_info = false;
+ doc_info_values info;
+
+ if (values.check(doc_info_tags::type)) {
+ info.doc_type =
+ values.consume(doc_info_tags::type).get_quickbook().to_s();
+ info.doc_title = values.consume(doc_info_tags::title);
+ use_doc_info = !nested_file || qbk_version_n >= 106u;
+ }
+ else {
+ if (!nested_file) {
+ detail::outerr(state.current_file, pos.base())
+ << "No doc_info block." << std::endl;
+
+ ++state.error_count;
+
+ // Create a fake document info block in order to continue.
+ info.doc_type = "article";
+ info.doc_title = qbk_value(
+ state.current_file, pos.base(), pos.base(),
+ doc_info_tags::type);
+ use_doc_info = true;
+ }
+ }
+
+ std::vector<std::string> duplicates;
+
+ info.escaped_attributes =
+ consume_multiple_values(values, doc_info_tags::escaped_attribute);
+
+ info.qbk_version =
+ consume_list(values, doc_attributes::qbk_version, &duplicates);
+ info.compatibility_mode = consume_list(
+ values, doc_attributes::compatibility_mode, &duplicates);
+ consume_multiple_values(values, doc_attributes::source_mode);
+
+ info.id =
+ consume_value_in_list(values, doc_info_attributes::id, &duplicates);
+ info.dirname = consume_value_in_list(
+ values, doc_info_attributes::dirname, &duplicates);
+ info.last_revision = consume_value_in_list(
+ values, doc_info_attributes::last_revision, &duplicates);
+ info.purpose = consume_value_in_list(
+ values, doc_info_attributes::purpose, &duplicates);
+ info.categories =
+ consume_multiple_values(values, doc_info_attributes::category);
+ info.lang = consume_value_in_list(
+ values, doc_info_attributes::lang, &duplicates);
+ info.version = consume_value_in_list(
+ values, doc_info_attributes::version, &duplicates);
+ info.authors =
+ consume_multiple_values(values, doc_info_attributes::authors);
+ info.copyrights =
+ consume_multiple_values(values, doc_info_attributes::copyright);
+ info.license = consume_value_in_list(
+ values, doc_info_attributes::license, &duplicates);
+ info.biblioids =
+ consume_multiple_values(values, doc_info_attributes::biblioid);
+ info.xmlbase = consume_value_in_list(
+ values, doc_info_attributes::xmlbase, &duplicates);
+
+ values.finish();
+
+ if (!duplicates.empty()) {
+ detail::outwarn(state.current_file->path)
+ << (duplicates.size() > 1 ? "Duplicate attributes"
+ : "Duplicate attribute")
+ << ":" << boost::algorithm::join(duplicates, ", ") << "\n";
+ }
+
+ if (!include_doc_id.empty())
+ info.include_doc_id_ = include_doc_id.get_quickbook().to_s();
+ if (!info.id.empty()) info.id_ = info.id.get_quickbook().to_s();
+
+ // Quickbook version
+
+ unsigned new_version =
+ get_version(state, use_doc_info, info.qbk_version);
+
+ if (new_version != qbk_version_n) {
+ if (classify_version(new_version) == version_dev) {
+ detail::outwarn(state.current_file->path)
+ << "Quickbook " << (new_version / 100) << "."
+ << (new_version % 100)
+ << " is still under development and is "
+ "likely to change in the future."
+ << std::endl;
+ }
+ }
+
+ if (new_version) {
+ qbk_version_n = new_version;
+ }
+ else if (use_doc_info) {
+ // hard code quickbook version to v1.1
+ qbk_version_n = 101;
+ detail::outwarn(state.current_file, pos.base())
+ << "Quickbook version undefined. "
+ "Version 1.1 is assumed"
+ << std::endl;
+ }
+
+ state.current_file->version(qbk_version_n);
+
+ // Compatibility Version
+
+ unsigned compatibility_version =
+ get_version(state, use_doc_info, info.compatibility_mode);
+
+ if (!compatibility_version) {
+ compatibility_version =
+ use_doc_info ? qbk_version_n
+ : state.document.compatibility_version();
+ }
+
+ // Start file, finish here if not generating document info.
+
+ if (!use_doc_info) {
+ state.document.start_file(
+ compatibility_version, info.include_doc_id_, info.id_,
+ info.doc_title);
+ return "";
+ }
+
+ info.id_placeholder = state.document.start_file_with_docinfo(
+ compatibility_version, info.include_doc_id_, info.id_,
+ info.doc_title);
+
+ // Make sure we really did have a document info block.
+
+ assert(info.doc_title.check() && !info.doc_type.empty());
+
+ // Set xmlbase
+
+ // std::string xmlbase_value;
+
+ if (!info.xmlbase.empty()) {
+ path_parameter x = check_xinclude_path(info.xmlbase, state);
+
+ if (x.type == path_parameter::path) {
+ quickbook_path path = resolve_xinclude_path(x.value, state);
+
+ if (!fs::is_directory(path.file_path)) {
+ detail::outerr(
+ info.xmlbase.get_file(), info.xmlbase.get_position())
+ << "xmlbase \"" << info.xmlbase.get_quickbook()
+ << "\" isn't a directory." << std::endl;
+
+ ++state.error_count;
+ }
+ else {
+ info.xmlbase_value =
+ dir_path_to_url(path.abstract_file_path);
+ state.xinclude_base = path.file_path;
+ }
+ }
+ }
+
+ // Warn about invalid fields
+
+ if (info.doc_type != "library") {
+ std::vector<std::string> invalid_attributes;
+
+ if (!info.purpose.empty()) invalid_attributes.push_back("purpose");
+
+ if (!info.categories.empty())
+ invalid_attributes.push_back("category");
+
+ if (!info.dirname.empty()) invalid_attributes.push_back("dirname");
+
+ if (!invalid_attributes.empty()) {
+ detail::outwarn(state.current_file->path)
+ << (invalid_attributes.size() > 1 ? "Invalid attributes"
+ : "Invalid attribute")
+ << " for '" << info.doc_type << " document info': "
+ << boost::algorithm::join(invalid_attributes, ", ") << "\n";
+ }
+ }
+
+ return write_boostbook_header(state, info, nested_file);
+ }
+
+ std::string write_boostbook_header(
+ quickbook::state& state, doc_info_values const& info, bool nested_file)
+ {
+ // Write out header
+
+ if (!nested_file) {
+ state.out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ << "<!DOCTYPE " << info.doc_type
+ << " PUBLIC \"-//Boost//DTD BoostBook XML V1.0//EN\"\n"
+ << " "
+ "\"http://www.boost.org/tools/boostbook/dtd/"
+ "boostbook.dtd\">\n";
+ }
+
+ state.out << '<' << info.doc_type << "\n"
+ << " id=\"" << info.id_placeholder << "\"\n";
+
+ if (!info.lang.empty()) {
+ state.out << " lang=\"" << doc_info_output(info.lang, 106)
+ << "\"\n";
+ }
+
+ if (info.doc_type == "library" && !info.doc_title.empty()) {
+ state.out << " name=\"" << doc_info_output(info.doc_title, 106)
+ << "\"\n";
+ }
+
+ // Set defaults for dirname + last_revision
+
+ if (!info.dirname.empty() || info.doc_type == "library") {
+ state.out << " dirname=\"";
+ if (!info.dirname.empty()) {
+ state.out << doc_info_output(info.dirname, 106);
+ }
+ else if (!info.id_.empty()) {
+ state.out << info.id_;
+ }
+ else if (!info.include_doc_id_.empty()) {
+ state.out << info.include_doc_id_;
+ }
+ else if (!info.doc_title.empty()) {
+ state.out << detail::make_identifier(
+ info.doc_title.get_quickbook());
+ }
+ else {
+ state.out << "library";
+ }
+
+ state.out << "\"\n";
+ }
+
+ state.out << " last-revision=\"";
+ if (!info.last_revision.empty()) {
+ state.out << doc_info_output(info.last_revision, 106);
+ }
+ else {
+ // default value for last-revision is now
+
+ char strdate[64];
+ strftime(
+ strdate, sizeof(strdate),
+ (debug_mode ? "DEBUG MODE Date: %Y/%m/%d %H:%M:%S $"
+ : "$" /* prevent CVS substitution */
+ "Date: %Y/%m/%d %H:%M:%S $"),
+ current_gm_time);
+
+ state.out << strdate;
+ }
+
+ state.out << "\" \n";
+
+ if (!info.xmlbase_value.empty()) {
+ state.out << " xml:base=\"" << info.xmlbase_value << "\"\n";
+ }
+
+ state.out << " xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n";
+
+ std::ostringstream tmp;
+
+ if (!info.authors.empty()) {
+ tmp << " <authorgroup>\n";
+ QUICKBOOK_FOR (value_consumer author_values, info.authors) {
+ while (author_values.check()) {
+ value surname =
+ author_values.consume(doc_info_tags::author_surname);
+ value first =
+ author_values.consume(doc_info_tags::author_first);
+
+ tmp << " <author>\n"
+ << " <firstname>" << doc_info_output(first, 106)
+ << "</firstname>\n"
+ << " <surname>" << doc_info_output(surname, 106)
+ << "</surname>\n"
+ << " </author>\n";
+ }
+ }
+ tmp << " </authorgroup>\n";
+ }
+
+ QUICKBOOK_FOR (value_consumer copyright, info.copyrights) {
+ while (copyright.check()) {
+ tmp << "\n"
+ << " <copyright>\n";
+
+ while (copyright.check(doc_info_tags::copyright_year)) {
+ value year_start_value = copyright.consume();
+ int year_start = year_start_value.get_int();
+ int year_end =
+ copyright.check(doc_info_tags::copyright_year_end)
+ ? copyright.consume().get_int()
+ : year_start;
+
+ if (year_end < year_start) {
+ ++state.error_count;
+
+ detail::outerr(
+ state.current_file,
+ copyright.begin()->get_position())
+ << "Invalid year range: " << year_start << "-"
+ << year_end << "." << std::endl;
+ }
+
+ for (; year_start <= year_end; ++year_start)
+ tmp << " <year>" << year_start << "</year>\n";
+ }
+
+ tmp << " <holder>"
+ << doc_info_output(
+ copyright.consume(doc_info_tags::copyright_name),
+ 106)
+ << "</holder>\n"
+ << " </copyright>\n"
+ << "\n";
+ }
+ }
+
+ if (!info.license.empty()) {
+ tmp << " <legalnotice id=\""
+ << state.document.add_id("legal", id_category::generated)
+ << "\">\n"
+ << " <para>\n"
+ << " " << doc_info_output(info.license, 103) << "\n"
+ << " </para>\n"
+ << " </legalnotice>\n"
+ << "\n";
+ }
+
+ if (!info.purpose.empty()) {
+ tmp << " <" << info.doc_type << "purpose>\n"
+ << " " << doc_info_output(info.purpose, 103) << " </"
+ << info.doc_type << "purpose>\n"
+ << "\n";
+ }
+
+ QUICKBOOK_FOR (value_consumer category_values, info.categories) {
+ value category = category_values.optional_consume();
+ if (!category.empty()) {
+ tmp << " <" << info.doc_type << "category name=\"category:"
+ << doc_info_output(category, 106) << "\"></"
+ << info.doc_type << "category>\n"
+ << "\n";
+ }
+ category_values.finish();
+ }
+
+ QUICKBOOK_FOR (value_consumer biblioid, info.biblioids) {
+ value class_ = biblioid.consume(doc_info_tags::biblioid_class);
+ value value_ = biblioid.consume(doc_info_tags::biblioid_value);
+
+ tmp << " <biblioid class=\"" << class_.get_quickbook() << "\">"
+ << doc_info_output(value_, 106) << "</biblioid>"
+ << "\n";
+ biblioid.finish();
+ }
+
+ QUICKBOOK_FOR (value escaped, info.escaped_attributes) {
+ tmp << "<!--quickbook-escape-prefix-->" << escaped.get_quickbook()
+ << "<!--quickbook-escape-postfix-->";
+ }
+
+ if (info.doc_type != "library") {
+ write_document_title(state.out, info.doc_title, info.version);
+ }
+
+ std::string docinfo = tmp.str();
+ if (!docinfo.empty()) {
+ state.out << " <" << info.doc_type << "info>\n"
+ << docinfo << " </" << info.doc_type << "info>\n"
+ << "\n";
+ }
+
+ if (info.doc_type == "library") {
+ write_document_title(state.out, info.doc_title, info.version);
+ }
+
+ return info.doc_type;
+ }
+
+ void post(quickbook::state& state, std::string const& doc_type)
+ {
+ // We've finished generating our output. Here's what we'll do
+ // *after* everything else.
+
+ // Close any open sections.
+ if (!doc_type.empty() && state.document.section_level() > 1) {
+ if (state.strict_mode) {
+ detail::outerr(state.current_file->path)
+ << "Missing [endsect] detected at end of file (strict "
+ "mode)."
+ << std::endl;
+ ++state.error_count;
+ }
+ else {
+ detail::outwarn(state.current_file->path)
+ << "Missing [endsect] detected at end of file."
+ << std::endl;
+ }
+
+ while (state.document.section_level() > 1) {
+ state.out << "</section>";
+ state.document.end_section();
+ }
+ }
+
+ state.document.end_file();
+ if (!doc_type.empty()) state.out << "\n</" << doc_type << ">\n\n";
+ }
+
+ static void write_document_title(
+ collector& out, value const& title, value const& version)
+ {
+ if (!title.empty()) {
+ out << " <title>" << doc_info_output(title, 106);
+ if (!version.empty()) {
+ out << ' ' << doc_info_output(version, 106);
+ }
+ out << "</title>\n\n\n";
+ }
+ }
+}