]>
git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/quickbook/src/post_process.cpp
c40c5e24fbc156f7531e0b02a946e234229a8c97
1 /*=============================================================================
2 Copyright (c) 2005 2006 Joel de Guzman
3 http://spirit.sourceforge.net/
5 Use, modification and distribution is subject to the Boost Software
6 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 http://www.boost.org/LICENSE_1_0.txt)
8 =============================================================================*/
9 #include "post_process.hpp"
13 #include <boost/bind.hpp>
14 #include <boost/spirit/include/classic_core.hpp>
15 #include <boost/spirit/include/phoenix1_operators.hpp>
16 #include <boost/spirit/include/phoenix1_primitives.hpp>
20 namespace cl
= boost::spirit::classic
;
21 namespace ph
= phoenix
;
22 typedef std::string::const_iterator iter_type
;
26 pretty_printer(std::string
& out_
, int& current_indent_
, int linewidth_
)
29 , current_indent(current_indent_
)
32 , linewidth(linewidth_
)
38 BOOST_ASSERT(current_indent
>= 0); // this should not happen!
39 for (int i
= 0; i
< current_indent
; ++i
)
41 column
= current_indent
;
46 out
.erase(out
.find_last_not_of(' ') + 1); // trim trailing spaces
56 bool line_is_empty() const
58 for (iter_type i
= out
.end() - (column
- current_indent
);
59 i
!= out
.end(); ++i
) {
60 if (*i
!= ' ') return false;
67 // make sure we are at the proper indent position
68 if (column
!= current_indent
) {
69 if (column
> current_indent
) {
70 if (line_is_empty()) {
71 // trim just enough trailing spaces down to
72 // current_indent position
74 out
.end() - (column
- current_indent
), out
.end());
75 column
= current_indent
;
78 // nope, line is not empty. do a hard CR
83 // will this happen? (i.e. column <= current_indent)
84 while (column
!= current_indent
) {
94 // Print a char. Attempt to break the line if we are exceeding
95 // the target linewidth. The linewidth is not an absolute limit.
96 // There are many cases where a line will exceed the linewidth
97 // and there is no way to properly break the line. Preformatted
98 // code that exceeds the linewidth are examples. We cannot break
99 // preformatted code. We shall not attempt to be very strict with
100 // line breaking. What's more important is to have a reproducable
101 // output (i.e. processing two logically equivalent xml files
102 // results in two lexically equivalent xml files). *** pretty
103 // formatting is a secondary goal ***
105 // Strings will occur only in tag attributes. Normal content
106 // will have " instead. We shall deal only with tag
108 if (ch
== '"') in_string
= !in_string
; // don't break strings!
110 if (!in_string
&& std::isspace(static_cast<unsigned char>(ch
))) {
111 // we can break spaces if they are not inside strings
112 if (!std::isspace(static_cast<unsigned char>(prev
))) {
113 if (column
>= linewidth
) {
115 if (column
== 0 && ch
== ' ') {
127 // we can break tag boundaries and stuff after
128 // delimiters if they are not inside strings
129 // and *only-if* the preceding char is a space
130 if (!in_string
&& column
>= linewidth
&&
132 std::isspace(static_cast<unsigned char>(prev
))))
141 void print(iter_type f
, iter_type l
)
143 for (iter_type i
= f
; i
!= l
; ++i
)
147 void print_tag(iter_type f
, iter_type l
, bool is_flow_tag
)
153 // This is not a flow tag, so, we're going to do a
154 // carriage return anyway. Let us remove extra right
156 std::string
str(f
, l
);
157 BOOST_ASSERT(f
!= l
); // this should not happen
158 iter_type i
= str
.end();
159 while (i
!= str
.begin() &&
160 std::isspace(static_cast<unsigned char>(*(i
- 1))))
162 print(str
.begin(), i
);
174 pretty_printer
& operator=(pretty_printer
const&);
177 char const* html_block_tags_
[] = {
178 "div", "p", "blockquote", "address", "h1", "h2", "h3",
179 "h4", "h5", "h6", "ul", "ol", "li", "dl",
180 "dt", "dd", "table", "tr", "th", "td", "tbody",
181 "thead", "form", "fieldset", "hr", "noscript", "html", "body"};
183 char const* block_tags_
[] = {
184 "author", "blockquote", "bridgehead", "callout",
185 "calloutlist", "caution", "copyright", "entry",
186 "important", "informaltable", "itemizedlist", "legalnotice",
187 "listitem", "note", "orderedlist", "para",
188 "row", "section", "simpara", "table",
189 "tbody", "textobject", "tgroup", "thead",
190 "tip", "variablelist", "varlistentry", "warning",
191 "xml", "xi:include"};
193 char const* doc_types_
[] = {"book", "article", "library", "chapter",
194 "part", "appendix", "preface", "qandadiv",
195 "qandaset", "reference", "set"};
199 tidy_compiler(std::string
& out_
, int linewidth_
, bool is_html
)
202 , printer(out_
, current_indent
, linewidth_
)
205 static std::size_t const n_block_tags
=
206 sizeof(html_block_tags_
) / sizeof(char const*);
207 for (std::size_t i
= 0; i
!= n_block_tags
; ++i
) {
208 block_tags
.insert(html_block_tags_
[i
]);
212 static std::size_t const n_block_tags
=
213 sizeof(block_tags_
) / sizeof(char const*);
214 for (std::size_t i
= 0; i
!= n_block_tags
; ++i
) {
215 block_tags
.insert(block_tags_
[i
]);
218 static std::size_t const n_doc_types
=
219 sizeof(doc_types_
) / sizeof(char const*);
220 for (std::size_t i
= 0; i
!= n_doc_types
; ++i
) {
221 block_tags
.insert(doc_types_
[i
]);
222 block_tags
.insert(doc_types_
[i
] + std::string("info"));
223 block_tags
.insert(doc_types_
[i
] + std::string("purpose"));
228 bool is_flow_tag(std::string
const& tag
)
230 return block_tags
.find(tag
) == block_tags
.end();
233 std::set
<std::string
> block_tags
;
234 std::stack
<std::string
> tags
;
237 pretty_printer printer
;
238 std::string current_tag
;
241 tidy_compiler
& operator=(tidy_compiler
const&);
244 struct tidy_grammar
: cl::grammar
<tidy_grammar
>
246 tidy_grammar(tidy_compiler
& state_
, int indent_
, bool is_html_
)
247 : state(state_
), indent(indent_
), is_html(is_html_
)
251 template <typename Scanner
> struct definition
253 definition(tidy_grammar
const& self
)
257 tag
= (cl::lexeme_d
[+(cl::alnum_p
| '_' | ':')]) [boost::bind(&tidy_grammar::do_tag
, &self
, _1
, _2
)];
259 code
= cl::eps_p(ph::var(self
.is_html
))
261 >> cl::lexeme_d
[cl::str_p("pre")]
262 >> *(cl::anychar_p
- '>')
264 >> *(cl::anychar_p
- "</pre>")
266 >> cl::lexeme_d
[">" >> *cl::space_p
]
267 | cl::eps_p(!ph::var(self
.is_html
))
268 >> "<programlisting>"
269 >> *(cl::anychar_p
- "</programlisting>")
270 >> "</programlisting"
271 >> cl::lexeme_d
[">" >> *cl::space_p
]
274 // What's the business of cl::lexeme_d['>' >> *cl::space_p]; ?
275 // It is there to preserve the space after the tag that is
276 // otherwise consumed by the cl::space_p skipper.
279 cl::str_p("<!--quickbook-escape-prefix-->") >>
280 (*(cl::anychar_p
- cl::str_p("<!--quickbook-escape-postfix-->")))
282 boost::bind(&tidy_grammar::do_escape
, &self
, _1
, _2
)
286 cl::str_p("<!--quickbook-escape-postfix-->") >>
289 boost::bind(&tidy_grammar::do_escape_post
, &self
, _1
, _2
)
294 start_tag
= '<' >> tag
>> *(cl::anychar_p
- '>') >> cl::lexeme_d
['>' >> *cl::space_p
];
296 '<' >> tag
>> *(cl::anychar_p
- ("/>" | cl::ch_p('>'))) >> cl::lexeme_d
["/>" >> *cl::space_p
]
297 | "<?" >> tag
>> *(cl::anychar_p
- '?') >> cl::lexeme_d
["?>" >> *cl::space_p
]
298 | "<!--" >> *(cl::anychar_p
- "-->") >> cl::lexeme_d
["-->" >> *cl::space_p
]
299 | "<!" >> tag
>> *(cl::anychar_p
- '>') >> cl::lexeme_d
['>' >> *cl::space_p
]
301 content
= cl::lexeme_d
[ +(cl::anychar_p
- '<') ];
302 end_tag
= "</" >> +(cl::anychar_p
- '>') >> cl::lexeme_d
['>' >> *cl::space_p
];
306 | code
[boost::bind(&tidy_grammar::do_code
, &self
, _1
, _2
)]
307 | start_end_tag
[boost::bind(&tidy_grammar::do_start_end_tag
, &self
, _1
, _2
)]
308 | start_tag
[boost::bind(&tidy_grammar::do_start_tag
, &self
, _1
, _2
)]
309 | end_tag
[boost::bind(&tidy_grammar::do_end_tag
, &self
, _1
, _2
)]
310 | content
[boost::bind(&tidy_grammar::do_content
, &self
, _1
, _2
)]
318 cl::rule
<Scanner
> const& start() { return tidy
; }
320 cl::rule
<Scanner
> tidy
, tag
, start_tag
, start_end_tag
, content
,
321 end_tag
, markup
, code
, escape
;
324 void do_escape_post(iter_type f
, iter_type l
) const
326 for (iter_type i
= f
; i
!= l
; ++i
)
330 void do_escape(iter_type f
, iter_type l
) const
332 while (f
!= l
&& std::isspace(*f
)) {
335 while (f
!= l
&& std::isspace(*(l
- 1))) {
338 for (iter_type i
= f
; i
!= l
; ++i
) {
343 void do_code(iter_type f
, iter_type l
) const
345 state
.printer
.trim_spaces();
346 if (state
.out
[state
.out
.size() - 1] != '\n') state
.out
+= '\n';
348 // trim trailing space from after closing tag
349 while (f
!= l
&& std::isspace(*(l
- 1))) {
353 // print the string taking care of line
354 // ending CR/LF platform issues
355 for (iter_type i
= f
; i
!= l
;) {
357 state
.printer
.trim_spaces();
360 if (i
!= l
&& *i
== '\r') {
364 else if (*i
== '\r') {
365 state
.printer
.trim_spaces();
368 if (i
!= l
&& *i
== '\n') {
378 state
.printer
.indent();
381 void do_tag(iter_type f
, iter_type l
) const
383 state
.current_tag
= std::string(f
, l
);
386 void do_start_end_tag(iter_type f
, iter_type l
) const
388 bool is_flow_tag
= state
.is_flow_tag(state
.current_tag
);
389 if (!is_flow_tag
) state
.printer
.align_indent();
390 state
.printer
.print_tag(f
, l
, is_flow_tag
);
391 if (!is_flow_tag
) state
.printer
.break_line();
394 void do_start_tag(iter_type f
, iter_type l
) const
396 state
.tags
.push(state
.current_tag
);
397 bool is_flow_tag
= state
.is_flow_tag(state
.current_tag
);
398 if (!is_flow_tag
) state
.printer
.align_indent();
399 state
.printer
.print_tag(f
, l
, is_flow_tag
);
401 state
.current_indent
+= indent
;
402 state
.printer
.break_line();
406 void do_content(iter_type f
, iter_type l
) const
408 state
.printer
.print(f
, l
);
411 void do_end_tag(iter_type f
, iter_type l
) const
413 if (state
.tags
.empty())
414 throw quickbook::post_process_failure("Mismatched tags.");
416 bool is_flow_tag
= state
.is_flow_tag(state
.tags
.top());
418 state
.current_indent
-= indent
;
419 state
.printer
.align_indent();
421 state
.printer
.print_tag(f
, l
, is_flow_tag
);
422 if (!is_flow_tag
) state
.printer
.break_line();
426 tidy_compiler
& state
;
431 tidy_grammar
& operator=(tidy_grammar
const&);
434 std::string
post_process(
435 std::string
const& in
, int indent
, int linewidth
, bool is_html
)
437 if (indent
== -1) indent
= 2; // set default to 2
438 if (linewidth
== -1) linewidth
= 80; // set default to 80
441 tidy_compiler
state(tidy
, linewidth
, is_html
);
442 tidy_grammar
g(state
, indent
, is_html
);
443 cl::parse_info
<iter_type
> r
=
444 parse(in
.begin(), in
.end(), g
, cl::space_p
);
449 throw quickbook::post_process_failure("Post Processing Failed.");