]>
Commit | Line | Data |
---|---|---|
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 |
25 | using namespace boost::placeholders; |
26 | ||
7c673cae FG |
27 | namespace 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 | } |