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