diff options
Diffstat (limited to 'src/boost/tools/quickbook/src/doc_info_actions.cpp')
-rw-r--r-- | src/boost/tools/quickbook/src/doc_info_actions.cpp | 608 |
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"; + } + } +} |