]>
Commit | Line | Data |
---|---|---|
7b8c46c8 BP |
1 | # Copyright (c) 2010, 2011, 2012, 2015 Nicira, Inc. |
2 | # | |
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | # you may not use this file except in compliance with the License. | |
5 | # You may obtain a copy of the License at: | |
6 | # | |
7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
8 | # | |
9 | # Unless required by applicable law or agreed to in writing, software | |
10 | # distributed under the License is distributed on an "AS IS" BASIS, | |
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | # See the License for the specific language governing permissions and | |
13 | # limitations under the License. | |
14 | ||
15 | import re | |
16 | ||
17 | from ovs.db import error | |
18 | ||
4d9d1d9e | 19 | def text_to_nroff(s, font=r'\fR'): |
7b8c46c8 BP |
20 | def escape(match): |
21 | c = match.group(0) | |
22 | ||
23 | # In Roman type, let -- in XML be \- in nroff. That gives us a way to | |
24 | # write minus signs, which is important in some places in manpages. | |
25 | # | |
26 | # Bold in nroff usually represents literal text, where there's no | |
27 | # distinction between hyphens and minus sign. The convention in nroff | |
28 | # appears to be to use a minus sign in such cases, so we follow that | |
29 | # convention. | |
30 | # | |
31 | # Finally, we always output - as a minus sign when it is followed by a | |
32 | # digit. | |
33 | if c.startswith('-'): | |
34 | if c == '--' and font == r'\fR': | |
35 | return r'\-' | |
36 | if c != '-' or font in (r'\fB', r'\fL'): | |
37 | return c.replace('-', r'\-') | |
38 | else: | |
39 | return '-' | |
40 | ||
41 | if c == '\\': | |
42 | return r'\e' | |
43 | elif c == '"': | |
44 | return r'\(dq' | |
45 | elif c == "'": | |
46 | return r'\(cq' | |
47 | elif c == ".": | |
48 | # groff(7) says that . can be escaped by \. but in practice groff | |
49 | # still gives an error with \. at the beginning of a line. | |
7102846b | 50 | return r'\[char46]' |
7b8c46c8 BP |
51 | else: |
52 | raise error.Error("bad escape") | |
53 | ||
54 | # Escape - \ " ' . as needed by nroff. | |
55 | s = re.sub('(-[0-9]|--|[-"\'\\\\.])', escape, s) | |
56 | return s | |
57 | ||
4d9d1d9e BP |
58 | def escape_nroff_literal(s, font=r'\fB'): |
59 | return font + r'%s\fR' % text_to_nroff(s, font) | |
7b8c46c8 | 60 | |
4d9d1d9e | 61 | def inline_xml_to_nroff(node, font, to_upper=False): |
7b8c46c8 BP |
62 | if node.nodeType == node.TEXT_NODE: |
63 | if to_upper: | |
4d9d1d9e | 64 | return text_to_nroff(node.data.upper(), font) |
7b8c46c8 | 65 | else: |
4d9d1d9e | 66 | return text_to_nroff(node.data, font) |
7b8c46c8 | 67 | elif node.nodeType == node.ELEMENT_NODE: |
d75ef07f | 68 | if node.tagName in ['code', 'em', 'option', 'env']: |
7b8c46c8 BP |
69 | s = r'\fB' |
70 | for child in node.childNodes: | |
4d9d1d9e | 71 | s += inline_xml_to_nroff(child, r'\fB') |
7b8c46c8 BP |
72 | return s + font |
73 | elif node.tagName == 'ref': | |
74 | s = r'\fB' | |
75 | if node.hasAttribute('column'): | |
76 | s += node.attributes['column'].nodeValue | |
77 | if node.hasAttribute('key'): | |
78 | s += ':' + node.attributes['key'].nodeValue | |
79 | elif node.hasAttribute('table'): | |
80 | s += node.attributes['table'].nodeValue | |
81 | elif node.hasAttribute('group'): | |
82 | s += node.attributes['group'].nodeValue | |
83 | elif node.hasAttribute('db'): | |
84 | s += node.attributes['db'].nodeValue | |
85 | else: | |
86 | raise error.Error("'ref' lacks required attributes: %s" % node.attributes.keys()) | |
87 | return s + font | |
88 | elif node.tagName == 'var' or node.tagName == 'dfn': | |
89 | s = r'\fI' | |
90 | for child in node.childNodes: | |
4d9d1d9e | 91 | s += inline_xml_to_nroff(child, r'\fI') |
7b8c46c8 BP |
92 | return s + font |
93 | else: | |
94 | raise error.Error("element <%s> unknown or invalid here" % node.tagName) | |
95 | else: | |
96 | raise error.Error("unknown node %s in inline xml" % node) | |
97 | ||
98 | def pre_to_nroff(nodes, para, font): | |
99 | s = para + '\n.nf\n' | |
100 | for node in nodes: | |
101 | if node.nodeType != node.TEXT_NODE: | |
102 | fatal("<pre> element may only have text children") | |
103 | for line in node.data.split('\n'): | |
4d9d1d9e | 104 | s += escape_nroff_literal(line, font) + '\n.br\n' |
7b8c46c8 BP |
105 | s += '.fi\n' |
106 | return s | |
107 | ||
4d9d1d9e | 108 | def block_xml_to_nroff(nodes, para='.PP'): |
7b8c46c8 BP |
109 | s = '' |
110 | for node in nodes: | |
111 | if node.nodeType == node.TEXT_NODE: | |
4d9d1d9e | 112 | s += text_to_nroff(node.data) |
7b8c46c8 BP |
113 | s = s.lstrip() |
114 | elif node.nodeType == node.ELEMENT_NODE: | |
115 | if node.tagName in ['ul', 'ol']: | |
116 | if s != "": | |
117 | s += "\n" | |
118 | s += ".RS\n" | |
119 | i = 0 | |
4d9d1d9e BP |
120 | for li_node in node.childNodes: |
121 | if (li_node.nodeType == node.ELEMENT_NODE | |
122 | and li_node.tagName == 'li'): | |
7b8c46c8 BP |
123 | i += 1 |
124 | if node.tagName == 'ul': | |
125 | s += ".IP \\(bu\n" | |
126 | else: | |
127 | s += ".IP %d. .25in\n" % i | |
4d9d1d9e BP |
128 | s += block_xml_to_nroff(li_node.childNodes, ".IP") |
129 | elif (li_node.nodeType != node.TEXT_NODE | |
130 | or not li_node.data.isspace()): | |
7b8c46c8 BP |
131 | raise error.Error("<%s> element may only have <li> children" % node.tagName) |
132 | s += ".RE\n" | |
133 | elif node.tagName == 'dl': | |
134 | if s != "": | |
135 | s += "\n" | |
136 | s += ".RS\n" | |
137 | prev = "dd" | |
4d9d1d9e BP |
138 | for li_node in node.childNodes: |
139 | if (li_node.nodeType == node.ELEMENT_NODE | |
140 | and li_node.tagName == 'dt'): | |
7b8c46c8 BP |
141 | if prev == 'dd': |
142 | s += '.TP\n' | |
143 | else: | |
144 | s += '.TQ .5in\n' | |
145 | prev = 'dt' | |
4d9d1d9e BP |
146 | elif (li_node.nodeType == node.ELEMENT_NODE |
147 | and li_node.tagName == 'dd'): | |
7b8c46c8 BP |
148 | if prev == 'dd': |
149 | s += '.IP\n' | |
150 | prev = 'dd' | |
4d9d1d9e BP |
151 | elif (li_node.nodeType != node.TEXT_NODE |
152 | or not li_node.data.isspace()): | |
7b8c46c8 | 153 | raise error.Error("<dl> element may only have <dt> and <dd> children") |
4d9d1d9e | 154 | s += block_xml_to_nroff(li_node.childNodes, ".IP") |
7b8c46c8 BP |
155 | s += ".RE\n" |
156 | elif node.tagName == 'p': | |
157 | if s != "": | |
158 | if not s.endswith("\n"): | |
159 | s += "\n" | |
160 | s += para + "\n" | |
4d9d1d9e | 161 | s += block_xml_to_nroff(node.childNodes, para) |
7b8c46c8 BP |
162 | elif node.tagName in ('h1', 'h2', 'h3'): |
163 | if s != "": | |
164 | if not s.endswith("\n"): | |
165 | s += "\n" | |
166 | nroffTag = {'h1': 'SH', 'h2': 'SS', 'h3': 'ST'}[node.tagName] | |
167 | s += '.%s "' % nroffTag | |
168 | for child_node in node.childNodes: | |
4d9d1d9e | 169 | s += inline_xml_to_nroff(child_node, r'\fR', |
7b8c46c8 BP |
170 | to_upper=(nroffTag == 'SH')) |
171 | s += '"\n' | |
172 | elif node.tagName == 'pre': | |
173 | fixed = node.getAttribute('fixed') | |
174 | if fixed == 'yes': | |
175 | font = r'\fL' | |
176 | else: | |
177 | font = r'\fB' | |
178 | s += pre_to_nroff(node.childNodes, para, font) | |
179 | else: | |
4d9d1d9e | 180 | s += inline_xml_to_nroff(node, r'\fR') |
7b8c46c8 BP |
181 | else: |
182 | raise error.Error("unknown node %s in block xml" % node) | |
183 | if s != "" and not s.endswith('\n'): | |
184 | s += '\n' | |
185 | return s |