]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/tools/quickbook/src/doc_info_actions.cpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / tools / quickbook / src / doc_info_actions.cpp
CommitLineData
7c673cae
FG
1/*=============================================================================
2 Copyright (c) 2002 2004 2006 Joel de Guzman
3 Copyright (c) 2004 Eric Niebler
4 Copyright (c) 2005 Thomas Guest
5 http://spirit.sourceforge.net/
6
7 Use, modification and distribution is subject to the Boost Software
8 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
9 http://www.boost.org/LICENSE_1_0.txt)
10=============================================================================*/
11#include <sstream>
7c673cae 12#include <boost/algorithm/string/join.hpp>
20effc67 13#include <boost/bind/bind.hpp>
7c673cae 14#include <boost/filesystem/operations.hpp>
7c673cae
FG
15#include "doc_info_tags.hpp"
16#include "document_state.hpp"
11fdf7f2 17#include "files.hpp"
92f5a8d4 18#include "for.hpp"
b32b8144 19#include "path.hpp"
11fdf7f2
TL
20#include "quickbook.hpp"
21#include "state.hpp"
22#include "stream.hpp"
23#include "utils.hpp"
7c673cae 24
20effc67
TL
25using namespace boost::placeholders;
26
7c673cae
FG
27namespace quickbook
28{
92f5a8d4
TL
29 struct doc_info_values
30 {
31 std::string doc_type;
32 value doc_title;
33 std::vector<value> escaped_attributes;
34 value qbk_version, compatibility_mode;
35 value id, dirname, last_revision, purpose;
36 std::vector<value> categories;
37 value lang, version;
38 std::vector<value> authors;
39 std::vector<value> copyrights;
40 value license;
41 std::vector<value> biblioids;
42 value xmlbase;
43 std::string xmlbase_value;
44
45 std::string id_placeholder;
46 std::string include_doc_id_, id_;
47 };
48
11fdf7f2
TL
49 static void write_document_title(
50 collector& out, value const& title, value const& version);
92f5a8d4
TL
51 std::string write_boostbook_header(
52 quickbook::state& state, doc_info_values const& info, bool nested_file);
11fdf7f2 53
7c673cae
FG
54 static std::string doc_info_output(value const& p, unsigned version)
55 {
56 if (qbk_version_n < version) {
b32b8144 57 std::string value = p.get_quickbook().to_s();
7c673cae
FG
58 value.erase(value.find_last_not_of(" \t") + 1);
59 return value;
60 }
61 else {
62 return p.get_encoded();
63 }
64 }
65
b32b8144
FG
66 char const* doc_info_attribute_name(value::tag_type tag)
67 {
11fdf7f2
TL
68 return doc_attributes::is_tag(tag) ? doc_attributes::name(tag)
69 : doc_info_attributes::name(tag);
b32b8144
FG
70 }
71
7c673cae
FG
72 // Each docinfo attribute is stored in a value list, these are then stored
73 // in a sorted value list. The following convenience methods extract all the
74 // values for an attribute tag.
75
76 // Expecting at most one attribute, with several values in the list.
11fdf7f2
TL
77 value consume_list(
78 value_consumer& c,
79 value::tag_type tag,
80 std::vector<std::string>* duplicates)
7c673cae
FG
81 {
82 value p;
83
84 int count = 0;
11fdf7f2 85 while (c.check(tag)) {
7c673cae
FG
86 p = c.consume();
87 ++count;
88 }
89
11fdf7f2 90 if (count > 1) duplicates->push_back(doc_info_attribute_name(tag));
7c673cae
FG
91
92 return p;
93 }
94
95 // Expecting at most one attribute, with a single value, so extract that
96 // immediately.
11fdf7f2
TL
97 value consume_value_in_list(
98 value_consumer& c,
99 value::tag_type tag,
100 std::vector<std::string>* duplicates)
7c673cae
FG
101 {
102 value l = consume_list(c, tag, duplicates);
11fdf7f2 103 if (l.empty()) return l;
7c673cae
FG
104
105 assert(l.is_list());
106 value_consumer c2 = l;
107 value p = c2.consume();
108 c2.finish();
109
110 return p;
111 }
112
113 // Any number of attributes, so stuff them into a vector.
11fdf7f2
TL
114 std::vector<value> consume_multiple_values(
115 value_consumer& c, value::tag_type tag)
7c673cae
FG
116 {
117 std::vector<value> values;
11fdf7f2
TL
118
119 while (c.check(tag)) {
7c673cae
FG
120 values.push_back(c.consume());
121 }
11fdf7f2 122
7c673cae
FG
123 return values;
124 }
125
11fdf7f2
TL
126 enum version_state
127 {
128 version_unknown,
129 version_stable,
130 version_dev
131 };
132 version_state classify_version(unsigned v)
133 {
134 return v < 100u ? version_unknown
135 : v <= 107u ? version_stable :
136 // v <= 107u ? version_dev :
137 version_unknown;
b32b8144
FG
138 }
139
11fdf7f2
TL
140 unsigned get_version(
141 quickbook::state& state, bool using_docinfo, value version)
7c673cae
FG
142 {
143 unsigned result = 0;
11fdf7f2 144
7c673cae
FG
145 if (!version.empty()) {
146 value_consumer version_values(version);
11fdf7f2
TL
147 bool before_docinfo =
148 version_values.optional_consume(doc_info_tags::before_docinfo)
149 .check();
7c673cae
FG
150 int major_verison = version_values.consume().get_int();
151 int minor_verison = version_values.consume().get_int();
152 version_values.finish();
11fdf7f2 153
7c673cae 154 if (before_docinfo || using_docinfo) {
11fdf7f2
TL
155 result =
156 ((unsigned)major_verison * 100) + (unsigned)minor_verison;
157
158 if (classify_version(result) == version_unknown) {
7c673cae 159 detail::outerr(state.current_file->path)
11fdf7f2
TL
160 << "Unknown version: " << major_verison << "."
161 << minor_verison << std::endl;
7c673cae
FG
162 ++state.error_count;
163 }
164 }
165 }
11fdf7f2 166
7c673cae
FG
167 return result;
168 }
169
11fdf7f2
TL
170 std::string pre(
171 quickbook::state& state,
172 parse_iterator pos,
173 value include_doc_id,
174 bool nested_file)
7c673cae
FG
175 {
176 // The doc_info in the file has been parsed. Here's what we'll do
177 // *before* anything else.
178 //
179 // If there isn't a doc info block, then values will be empty, so most
180 // of the following code won't actually do anything.
181
182 value_consumer values = state.values.release();
183
184 // Skip over invalid attributes
185
11fdf7f2
TL
186 while (values.check(value::default_tag))
187 values.consume();
7c673cae
FG
188
189 bool use_doc_info = false;
92f5a8d4 190 doc_info_values info;
7c673cae 191
11fdf7f2 192 if (values.check(doc_info_tags::type)) {
92f5a8d4 193 info.doc_type =
11fdf7f2 194 values.consume(doc_info_tags::type).get_quickbook().to_s();
92f5a8d4 195 info.doc_title = values.consume(doc_info_tags::title);
7c673cae
FG
196 use_doc_info = !nested_file || qbk_version_n >= 106u;
197 }
11fdf7f2
TL
198 else {
199 if (!nested_file) {
7c673cae 200 detail::outerr(state.current_file, pos.base())
11fdf7f2 201 << "No doc_info block." << std::endl;
7c673cae
FG
202
203 ++state.error_count;
204
205 // Create a fake document info block in order to continue.
92f5a8d4
TL
206 info.doc_type = "article";
207 info.doc_title = qbk_value(
11fdf7f2 208 state.current_file, pos.base(), pos.base(),
7c673cae
FG
209 doc_info_tags::type);
210 use_doc_info = true;
211 }
212 }
213
214 std::vector<std::string> duplicates;
215
92f5a8d4 216 info.escaped_attributes =
11fdf7f2 217 consume_multiple_values(values, doc_info_tags::escaped_attribute);
7c673cae 218
92f5a8d4 219 info.qbk_version =
11fdf7f2 220 consume_list(values, doc_attributes::qbk_version, &duplicates);
92f5a8d4 221 info.compatibility_mode = consume_list(
11fdf7f2 222 values, doc_attributes::compatibility_mode, &duplicates);
7c673cae
FG
223 consume_multiple_values(values, doc_attributes::source_mode);
224
92f5a8d4 225 info.id =
11fdf7f2 226 consume_value_in_list(values, doc_info_attributes::id, &duplicates);
92f5a8d4 227 info.dirname = consume_value_in_list(
11fdf7f2 228 values, doc_info_attributes::dirname, &duplicates);
92f5a8d4 229 info.last_revision = consume_value_in_list(
11fdf7f2 230 values, doc_info_attributes::last_revision, &duplicates);
92f5a8d4 231 info.purpose = consume_value_in_list(
11fdf7f2 232 values, doc_info_attributes::purpose, &duplicates);
92f5a8d4 233 info.categories =
11fdf7f2 234 consume_multiple_values(values, doc_info_attributes::category);
92f5a8d4 235 info.lang = consume_value_in_list(
11fdf7f2 236 values, doc_info_attributes::lang, &duplicates);
92f5a8d4 237 info.version = consume_value_in_list(
11fdf7f2 238 values, doc_info_attributes::version, &duplicates);
92f5a8d4 239 info.authors =
11fdf7f2 240 consume_multiple_values(values, doc_info_attributes::authors);
92f5a8d4 241 info.copyrights =
11fdf7f2 242 consume_multiple_values(values, doc_info_attributes::copyright);
92f5a8d4 243 info.license = consume_value_in_list(
11fdf7f2 244 values, doc_info_attributes::license, &duplicates);
92f5a8d4 245 info.biblioids =
11fdf7f2 246 consume_multiple_values(values, doc_info_attributes::biblioid);
92f5a8d4 247 info.xmlbase = consume_value_in_list(
11fdf7f2 248 values, doc_info_attributes::xmlbase, &duplicates);
7c673cae
FG
249
250 values.finish();
251
11fdf7f2 252 if (!duplicates.empty()) {
7c673cae 253 detail::outwarn(state.current_file->path)
11fdf7f2
TL
254 << (duplicates.size() > 1 ? "Duplicate attributes"
255 : "Duplicate attribute")
256 << ":" << boost::algorithm::join(duplicates, ", ") << "\n";
7c673cae
FG
257 }
258
7c673cae 259 if (!include_doc_id.empty())
92f5a8d4
TL
260 info.include_doc_id_ = include_doc_id.get_quickbook().to_s();
261 if (!info.id.empty()) info.id_ = info.id.get_quickbook().to_s();
7c673cae
FG
262
263 // Quickbook version
264
92f5a8d4
TL
265 unsigned new_version =
266 get_version(state, use_doc_info, info.qbk_version);
7c673cae 267
11fdf7f2
TL
268 if (new_version != qbk_version_n) {
269 if (classify_version(new_version) == version_dev) {
7c673cae 270 detail::outwarn(state.current_file->path)
11fdf7f2
TL
271 << "Quickbook " << (new_version / 100) << "."
272 << (new_version % 100)
7c673cae 273 << " is still under development and is "
11fdf7f2
TL
274 "likely to change in the future."
275 << std::endl;
7c673cae
FG
276 }
277 }
278
279 if (new_version) {
280 qbk_version_n = new_version;
281 }
282 else if (use_doc_info) {
283 // hard code quickbook version to v1.1
284 qbk_version_n = 101;
285 detail::outwarn(state.current_file, pos.base())
286 << "Quickbook version undefined. "
11fdf7f2
TL
287 "Version 1.1 is assumed"
288 << std::endl;
7c673cae
FG
289 }
290
291 state.current_file->version(qbk_version_n);
292
293 // Compatibility Version
294
295 unsigned compatibility_version =
92f5a8d4 296 get_version(state, use_doc_info, info.compatibility_mode);
7c673cae
FG
297
298 if (!compatibility_version) {
11fdf7f2
TL
299 compatibility_version =
300 use_doc_info ? qbk_version_n
301 : state.document.compatibility_version();
7c673cae
FG
302 }
303
304 // Start file, finish here if not generating document info.
305
11fdf7f2
TL
306 if (!use_doc_info) {
307 state.document.start_file(
92f5a8d4
TL
308 compatibility_version, info.include_doc_id_, info.id_,
309 info.doc_title);
7c673cae
FG
310 return "";
311 }
312
92f5a8d4
TL
313 info.id_placeholder = state.document.start_file_with_docinfo(
314 compatibility_version, info.include_doc_id_, info.id_,
315 info.doc_title);
7c673cae
FG
316
317 // Make sure we really did have a document info block.
318
92f5a8d4 319 assert(info.doc_title.check() && !info.doc_type.empty());
7c673cae
FG
320
321 // Set xmlbase
322
92f5a8d4 323 // std::string xmlbase_value;
7c673cae 324
92f5a8d4
TL
325 if (!info.xmlbase.empty()) {
326 path_parameter x = check_xinclude_path(info.xmlbase, state);
7c673cae 327
11fdf7f2 328 if (x.type == path_parameter::path) {
7c673cae
FG
329 quickbook_path path = resolve_xinclude_path(x.value, state);
330
11fdf7f2 331 if (!fs::is_directory(path.file_path)) {
92f5a8d4
TL
332 detail::outerr(
333 info.xmlbase.get_file(), info.xmlbase.get_position())
334 << "xmlbase \"" << info.xmlbase.get_quickbook()
11fdf7f2 335 << "\" isn't a directory." << std::endl;
7c673cae
FG
336
337 ++state.error_count;
338 }
11fdf7f2 339 else {
92f5a8d4
TL
340 info.xmlbase_value =
341 dir_path_to_url(path.abstract_file_path);
7c673cae
FG
342 state.xinclude_base = path.file_path;
343 }
344 }
345 }
346
347 // Warn about invalid fields
348
92f5a8d4 349 if (info.doc_type != "library") {
7c673cae
FG
350 std::vector<std::string> invalid_attributes;
351
92f5a8d4 352 if (!info.purpose.empty()) invalid_attributes.push_back("purpose");
7c673cae 353
92f5a8d4
TL
354 if (!info.categories.empty())
355 invalid_attributes.push_back("category");
7c673cae 356
92f5a8d4 357 if (!info.dirname.empty()) invalid_attributes.push_back("dirname");
7c673cae 358
11fdf7f2 359 if (!invalid_attributes.empty()) {
7c673cae 360 detail::outwarn(state.current_file->path)
11fdf7f2
TL
361 << (invalid_attributes.size() > 1 ? "Invalid attributes"
362 : "Invalid attribute")
92f5a8d4 363 << " for '" << info.doc_type << " document info': "
11fdf7f2 364 << boost::algorithm::join(invalid_attributes, ", ") << "\n";
7c673cae
FG
365 }
366 }
367
92f5a8d4
TL
368 return write_boostbook_header(state, info, nested_file);
369 }
370
371 std::string write_boostbook_header(
372 quickbook::state& state, doc_info_values const& info, bool nested_file)
373 {
7c673cae
FG
374 // Write out header
375
11fdf7f2 376 if (!nested_file) {
7c673cae 377 state.out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
92f5a8d4 378 << "<!DOCTYPE " << info.doc_type
11fdf7f2
TL
379 << " PUBLIC \"-//Boost//DTD BoostBook XML V1.0//EN\"\n"
380 << " "
381 "\"http://www.boost.org/tools/boostbook/dtd/"
382 "boostbook.dtd\">\n";
7c673cae
FG
383 }
384
92f5a8d4
TL
385 state.out << '<' << info.doc_type << "\n"
386 << " id=\"" << info.id_placeholder << "\"\n";
7c673cae 387
92f5a8d4
TL
388 if (!info.lang.empty()) {
389 state.out << " lang=\"" << doc_info_output(info.lang, 106)
390 << "\"\n";
7c673cae
FG
391 }
392
92f5a8d4
TL
393 if (info.doc_type == "library" && !info.doc_title.empty()) {
394 state.out << " name=\"" << doc_info_output(info.doc_title, 106)
11fdf7f2 395 << "\"\n";
7c673cae
FG
396 }
397
398 // Set defaults for dirname + last_revision
399
92f5a8d4 400 if (!info.dirname.empty() || info.doc_type == "library") {
7c673cae 401 state.out << " dirname=\"";
92f5a8d4
TL
402 if (!info.dirname.empty()) {
403 state.out << doc_info_output(info.dirname, 106);
7c673cae 404 }
92f5a8d4
TL
405 else if (!info.id_.empty()) {
406 state.out << info.id_;
7c673cae 407 }
92f5a8d4
TL
408 else if (!info.include_doc_id_.empty()) {
409 state.out << info.include_doc_id_;
7c673cae 410 }
92f5a8d4
TL
411 else if (!info.doc_title.empty()) {
412 state.out << detail::make_identifier(
413 info.doc_title.get_quickbook());
7c673cae
FG
414 }
415 else {
416 state.out << "library";
417 }
418
419 state.out << "\"\n";
420 }
421
422 state.out << " last-revision=\"";
92f5a8d4
TL
423 if (!info.last_revision.empty()) {
424 state.out << doc_info_output(info.last_revision, 106);
7c673cae 425 }
11fdf7f2 426 else {
7c673cae
FG
427 // default value for last-revision is now
428
429 char strdate[64];
430 strftime(
431 strdate, sizeof(strdate),
11fdf7f2
TL
432 (debug_mode ? "DEBUG MODE Date: %Y/%m/%d %H:%M:%S $"
433 : "$" /* prevent CVS substitution */
434 "Date: %Y/%m/%d %H:%M:%S $"),
435 current_gm_time);
7c673cae
FG
436
437 state.out << strdate;
438 }
439
440 state.out << "\" \n";
441
92f5a8d4
TL
442 if (!info.xmlbase_value.empty()) {
443 state.out << " xml:base=\"" << info.xmlbase_value << "\"\n";
7c673cae
FG
444 }
445
446 state.out << " xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n";
447
448 std::ostringstream tmp;
449
92f5a8d4 450 if (!info.authors.empty()) {
7c673cae 451 tmp << " <authorgroup>\n";
92f5a8d4 452 QUICKBOOK_FOR (value_consumer author_values, info.authors) {
7c673cae 453 while (author_values.check()) {
11fdf7f2
TL
454 value surname =
455 author_values.consume(doc_info_tags::author_surname);
456 value first =
457 author_values.consume(doc_info_tags::author_first);
458
7c673cae 459 tmp << " <author>\n"
11fdf7f2 460 << " <firstname>" << doc_info_output(first, 106)
7c673cae 461 << "</firstname>\n"
11fdf7f2 462 << " <surname>" << doc_info_output(surname, 106)
7c673cae
FG
463 << "</surname>\n"
464 << " </author>\n";
465 }
466 }
467 tmp << " </authorgroup>\n";
468 }
469
92f5a8d4 470 QUICKBOOK_FOR (value_consumer copyright, info.copyrights) {
11fdf7f2
TL
471 while (copyright.check()) {
472 tmp << "\n"
473 << " <copyright>\n";
474
475 while (copyright.check(doc_info_tags::copyright_year)) {
7c673cae
FG
476 value year_start_value = copyright.consume();
477 int year_start = year_start_value.get_int();
478 int year_end =
11fdf7f2
TL
479 copyright.check(doc_info_tags::copyright_year_end)
480 ? copyright.consume().get_int()
481 : year_start;
482
7c673cae
FG
483 if (year_end < year_start) {
484 ++state.error_count;
11fdf7f2
TL
485
486 detail::outerr(
487 state.current_file,
488 copyright.begin()->get_position())
489 << "Invalid year range: " << year_start << "-"
490 << year_end << "." << std::endl;
7c673cae 491 }
11fdf7f2
TL
492
493 for (; year_start <= year_end; ++year_start)
7c673cae
FG
494 tmp << " <year>" << year_start << "</year>\n";
495 }
11fdf7f2 496
7c673cae 497 tmp << " <holder>"
11fdf7f2
TL
498 << doc_info_output(
499 copyright.consume(doc_info_tags::copyright_name),
500 106)
7c673cae
FG
501 << "</holder>\n"
502 << " </copyright>\n"
11fdf7f2 503 << "\n";
7c673cae
FG
504 }
505 }
506
92f5a8d4 507 if (!info.license.empty()) {
7c673cae
FG
508 tmp << " <legalnotice id=\""
509 << state.document.add_id("legal", id_category::generated)
510 << "\">\n"
511 << " <para>\n"
92f5a8d4 512 << " " << doc_info_output(info.license, 103) << "\n"
7c673cae
FG
513 << " </para>\n"
514 << " </legalnotice>\n"
11fdf7f2 515 << "\n";
7c673cae
FG
516 }
517
92f5a8d4
TL
518 if (!info.purpose.empty()) {
519 tmp << " <" << info.doc_type << "purpose>\n"
520 << " " << doc_info_output(info.purpose, 103) << " </"
521 << info.doc_type << "purpose>\n"
11fdf7f2 522 << "\n";
7c673cae
FG
523 }
524
92f5a8d4 525 QUICKBOOK_FOR (value_consumer category_values, info.categories) {
b32b8144 526 value category = category_values.optional_consume();
11fdf7f2 527 if (!category.empty()) {
92f5a8d4
TL
528 tmp << " <" << info.doc_type << "category name=\"category:"
529 << doc_info_output(category, 106) << "\"></"
530 << info.doc_type << "category>\n"
11fdf7f2 531 << "\n";
7c673cae 532 }
b32b8144 533 category_values.finish();
7c673cae
FG
534 }
535
92f5a8d4 536 QUICKBOOK_FOR (value_consumer biblioid, info.biblioids) {
7c673cae
FG
537 value class_ = biblioid.consume(doc_info_tags::biblioid_class);
538 value value_ = biblioid.consume(doc_info_tags::biblioid_value);
11fdf7f2
TL
539
540 tmp << " <biblioid class=\"" << class_.get_quickbook() << "\">"
541 << doc_info_output(value_, 106) << "</biblioid>"
542 << "\n";
7c673cae
FG
543 biblioid.finish();
544 }
545
92f5a8d4 546 QUICKBOOK_FOR (value escaped, info.escaped_attributes) {
11fdf7f2
TL
547 tmp << "<!--quickbook-escape-prefix-->" << escaped.get_quickbook()
548 << "<!--quickbook-escape-postfix-->";
7c673cae
FG
549 }
550
92f5a8d4
TL
551 if (info.doc_type != "library") {
552 write_document_title(state.out, info.doc_title, info.version);
7c673cae
FG
553 }
554
555 std::string docinfo = tmp.str();
11fdf7f2 556 if (!docinfo.empty()) {
92f5a8d4
TL
557 state.out << " <" << info.doc_type << "info>\n"
558 << docinfo << " </" << info.doc_type << "info>\n"
11fdf7f2 559 << "\n";
7c673cae
FG
560 }
561
92f5a8d4
TL
562 if (info.doc_type == "library") {
563 write_document_title(state.out, info.doc_title, info.version);
7c673cae
FG
564 }
565
92f5a8d4 566 return info.doc_type;
7c673cae
FG
567 }
568
569 void post(quickbook::state& state, std::string const& doc_type)
570 {
571 // We've finished generating our output. Here's what we'll do
572 // *after* everything else.
573
574 // Close any open sections.
575 if (!doc_type.empty() && state.document.section_level() > 1) {
b32b8144
FG
576 if (state.strict_mode) {
577 detail::outerr(state.current_file->path)
11fdf7f2
TL
578 << "Missing [endsect] detected at end of file (strict "
579 "mode)."
b32b8144
FG
580 << std::endl;
581 ++state.error_count;
11fdf7f2
TL
582 }
583 else {
b32b8144
FG
584 detail::outwarn(state.current_file->path)
585 << "Missing [endsect] detected at end of file."
586 << std::endl;
587 }
7c673cae 588
11fdf7f2 589 while (state.document.section_level() > 1) {
7c673cae
FG
590 state.out << "</section>";
591 state.document.end_section();
592 }
593 }
594
595 state.document.end_file();
596 if (!doc_type.empty()) state.out << "\n</" << doc_type << ">\n\n";
597 }
598
11fdf7f2
TL
599 static void write_document_title(
600 collector& out, value const& title, value const& version)
7c673cae 601 {
11fdf7f2
TL
602 if (!title.empty()) {
603 out << " <title>" << doc_info_output(title, 106);
7c673cae
FG
604 if (!version.empty()) {
605 out << ' ' << doc_info_output(version, 106);
606 }
11fdf7f2 607 out << "</title>\n\n\n";
7c673cae
FG
608 }
609 }
610}