diff options
Diffstat (limited to 'tools/quickbook/src/doc_info_actions.cpp')
-rw-r--r-- | tools/quickbook/src/doc_info_actions.cpp | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/tools/quickbook/src/doc_info_actions.cpp b/tools/quickbook/src/doc_info_actions.cpp new file mode 100644 index 0000000000..e23466fd3d --- /dev/null +++ b/tools/quickbook/src/doc_info_actions.cpp @@ -0,0 +1,559 @@ +/*============================================================================= + 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/bind.hpp> +#include <boost/algorithm/string/join.hpp> +#include <boost/foreach.hpp> +#include <boost/filesystem/v3/operations.hpp> +#include "quickbook.hpp" +#include "utils.hpp" +#include "files.hpp" +#include "input_path.hpp" +#include "actions_class.hpp" +#include "doc_info_tags.hpp" +#include "id_manager.hpp" + +namespace quickbook +{ + static void write_document_title(collector& out, value const& title, value const& version); + + static std::string doc_info_output(value const& p, unsigned version) + { + if (qbk_version_n < version) { + std::string value = p.get_quickbook(); + value.erase(value.find_last_not_of(" \t") + 1); + return value; + } + else { + return p.get_encoded(); + } + } + + // 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_attributes::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_lists(value_consumer& c, value::tag_type tag) + { + std::vector<value> values; + + while(c.check(tag)) { + values.push_back(c.consume()); + } + + return values; + } + + unsigned get_version(quickbook::actions& actions, 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(result < 100 || result > 106) + { + detail::outerr(actions.current_file->path) + << "Unknown version: " + << major_verison + << "." + << minor_verison + << std::endl; + ++actions.error_count; + } + } + } + + return result; + } + + std::string pre(quickbook::actions& actions, 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 = actions.values.release(); + + // Skip over invalid attributes + + while (values.check(value::default_tag)) values.consume(); + + bool use_doc_info = false; + std::string doc_type; + value doc_title; + + if (values.check(doc_info_tags::type)) + { + doc_type = values.consume(doc_info_tags::type).get_quickbook(); + doc_title = values.consume(doc_info_tags::title); + use_doc_info = !nested_file || qbk_version_n >= 106u; + } + else + { + if (!nested_file) + { + detail::outerr(actions.current_file, pos.base()) + << "No doc_info block." + << std::endl; + + ++actions.error_count; + + // Create a fake document info block in order to continue. + doc_type = "article"; + doc_title = qbk_value(actions.current_file, + pos.base(), pos.base(), + doc_info_tags::type); + use_doc_info = true; + } + } + + std::vector<std::string> duplicates; + + value qbk_version = consume_list(values, doc_attributes::qbk_version, &duplicates); + value compatibility_mode = consume_list(values, doc_attributes::compatibility_mode, &duplicates); + consume_multiple_lists(values, doc_attributes::source_mode); + + value id = consume_value_in_list(values, doc_info_attributes::id, &duplicates); + value dirname = consume_value_in_list(values, doc_info_attributes::dirname, &duplicates); + value last_revision = consume_value_in_list(values, doc_info_attributes::last_revision, &duplicates); + value purpose = consume_value_in_list(values, doc_info_attributes::purpose, &duplicates); + std::vector<value> categories = consume_multiple_lists(values, doc_info_attributes::category); + value lang = consume_value_in_list(values, doc_info_attributes::lang, &duplicates); + value version = consume_value_in_list(values, doc_info_attributes::version, &duplicates); + std::vector<value> authors = consume_multiple_lists(values, doc_info_attributes::authors); + std::vector<value> copyrights = consume_multiple_lists(values, doc_info_attributes::copyright); + value license = consume_value_in_list(values, doc_info_attributes::license, &duplicates); + std::vector<value> biblioids = consume_multiple_lists(values, doc_info_attributes::biblioid); + value xmlbase = consume_value_in_list(values, doc_info_attributes::xmlbase, &duplicates); + + values.finish(); + + if(!duplicates.empty()) + { + detail::outwarn(actions.current_file->path) + << (duplicates.size() > 1 ? + "Duplicate attributes" : "Duplicate attribute") + << ":" << detail::utf8(boost::algorithm::join(duplicates, ", ")) + << "\n" + ; + } + + std::string include_doc_id_, id_; + + if (!include_doc_id.empty()) + include_doc_id_ = include_doc_id.get_quickbook(); + if (!id.empty()) + id_ = id.get_quickbook(); + + // Quickbook version + + unsigned new_version = get_version(actions, use_doc_info, qbk_version); + + if (new_version != qbk_version_n && new_version == 106) + { + detail::outwarn(actions.current_file->path) + << "Quickbook 1.6 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(actions.current_file, pos.base()) + << "Quickbook version undefined. " + "Version 1.1 is assumed" << std::endl; + } + + actions.current_file->version(qbk_version_n); + + // Compatibility Version + + unsigned compatibility_version = + get_version(actions, use_doc_info, compatibility_mode); + + if (!compatibility_version) { + compatibility_version = use_doc_info ? + qbk_version_n : actions.ids.compatibility_version(); + } + + // Start file, finish here if not generating document info. + + if (!use_doc_info) + { + actions.ids.start_file(compatibility_version, include_doc_id_, id_, + doc_title); + return ""; + } + + std::string id_placeholder = + actions.ids.start_file_with_docinfo( + compatibility_version, include_doc_id_, id_, doc_title); + + // Make sure we really did have a document info block. + + assert(doc_title.check() && !doc_type.empty()); + + // Set xmlbase + + std::string xmlbase_value; + + if (!xmlbase.empty()) + { + xinclude_path x = calculate_xinclude_path(xmlbase, actions); + + if (!fs::is_directory(x.path)) + { + detail::outerr(xmlbase.get_file(), xmlbase.get_position()) + << "xmlbase \"" + << detail::utf8(xmlbase.get_quickbook()) + << "\" isn't a directory." + << std::endl; + + ++actions.error_count; + } + else + { + xmlbase_value = x.uri; + actions.xinclude_base = x.path; + } + } + + // Warn about invalid fields + + if (doc_type != "library") + { + std::vector<std::string> invalid_attributes; + + if (!purpose.empty()) + invalid_attributes.push_back("purpose"); + + if (!categories.empty()) + invalid_attributes.push_back("category"); + + if (!dirname.empty()) + invalid_attributes.push_back("dirname"); + + if(!invalid_attributes.empty()) + { + detail::outwarn(actions.current_file->path) + << (invalid_attributes.size() > 1 ? + "Invalid attributes" : "Invalid attribute") + << " for '" << detail::utf8(doc_type) << " document info': " + << detail::utf8(boost::algorithm::join(invalid_attributes, ", ")) + << "\n" + ; + } + } + + // Write out header + + if (!nested_file) + { + actions.out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + << "<!DOCTYPE " + << doc_type + << " PUBLIC \"-//Boost//DTD BoostBook XML V1.0//EN\"\n" + << " \"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd\">\n" + ; + } + + actions.out << '<' << doc_type << "\n" + << " id=\"" + << id_placeholder + << "\"\n"; + + if(!lang.empty()) + { + actions.out << " lang=\"" + << doc_info_output(lang, 106) + << "\"\n"; + } + + if(doc_type == "library" && !doc_title.empty()) + { + actions.out << " name=\"" << doc_info_output(doc_title, 106) << "\"\n"; + } + + // Set defaults for dirname + last_revision + + if (!dirname.empty() || doc_type == "library") + { + actions.out << " dirname=\""; + if (!dirname.empty()) { + actions.out << doc_info_output(dirname, 106); + } + else if (!id_.empty()) { + actions.out << id_; + } + else if (!include_doc_id_.empty()) { + actions.out << include_doc_id_; + } + else if (!doc_title.empty()) { + actions.out << detail::make_identifier(doc_title.get_quickbook()); + } + else { + actions.out << "library"; + } + + actions.out << "\"\n"; + } + + actions.out << " last-revision=\""; + if (!last_revision.empty()) + { + actions.out << doc_info_output(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 + ); + + actions.out << strdate; + } + + actions.out << "\" \n"; + + if (!xmlbase.empty()) + { + actions.out << " xml:base=\"" + << xmlbase_value + << "\"\n"; + } + + actions.out << " xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n"; + + std::ostringstream tmp; + + if(!authors.empty()) + { + tmp << " <authorgroup>\n"; + BOOST_FOREACH(value_consumer author_values, 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"; + } + + BOOST_FOREACH(value_consumer copyright, 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) { + ++actions.error_count; + + detail::outerr(actions.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 (!license.empty()) + { + tmp << " <legalnotice id=\"" + << actions.ids.add_id("legal", id_category::generated) + << "\">\n" + << " <para>\n" + << " " << doc_info_output(license, 103) << "\n" + << " </para>\n" + << " </legalnotice>\n" + << "\n" + ; + } + + if (!purpose.empty()) + { + tmp << " <" << doc_type << "purpose>\n" + << " " << doc_info_output(purpose, 103) + << " </" << doc_type << "purpose>\n" + << "\n" + ; + } + + BOOST_FOREACH(value_consumer values, categories) { + value category = values.optional_consume(); + if(!category.empty()) { + tmp << " <" << doc_type << "category name=\"category:" + << doc_info_output(category, 106) + << "\"></" << doc_type << "category>\n" + << "\n" + ; + } + values.finish(); + } + + BOOST_FOREACH(value_consumer biblioid, 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(); + } + + if(doc_type != "library") { + write_document_title(actions.out, doc_title, version); + } + + std::string docinfo = tmp.str(); + if(!docinfo.empty()) + { + actions.out << " <" << doc_type << "info>\n" + << docinfo + << " </" << doc_type << "info>\n" + << "\n" + ; + } + + if(doc_type == "library") { + write_document_title(actions.out, doc_title, version); + } + + return doc_type; + } + + void post(quickbook::actions& actions, 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() && actions.ids.section_level() > 1) { + detail::outwarn(actions.current_file->path) + << "Missing [endsect] detected at end of file." + << std::endl; + + while(actions.ids.section_level() > 1) { + actions.out << "</section>"; + actions.ids.end_section(); + } + } + + actions.ids.end_file(); + if (!doc_type.empty()) actions.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"; + } + } +} |