##// END OF EJS Templates
rst2man: avoid italic for references...
Martin Geisler -
r9627:4515761e default
parent child Browse files
Show More
@@ -1,1112 +1,1112 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 # $Id: manpage.py 6110 2009-08-31 14:40:33Z grubert $
3 # $Id: manpage.py 6110 2009-08-31 14:40:33Z grubert $
4 # Author: Engelbert Gruber <grubert@users.sourceforge.net>
4 # Author: Engelbert Gruber <grubert@users.sourceforge.net>
5 # Copyright: This module is put into the public domain.
5 # Copyright: This module is put into the public domain.
6
6
7 """
7 """
8 Simple man page writer for reStructuredText.
8 Simple man page writer for reStructuredText.
9
9
10 Man pages (short for "manual pages") contain system documentation on unix-like
10 Man pages (short for "manual pages") contain system documentation on unix-like
11 systems. The pages are grouped in numbered sections:
11 systems. The pages are grouped in numbered sections:
12
12
13 1 executable programs and shell commands
13 1 executable programs and shell commands
14 2 system calls
14 2 system calls
15 3 library functions
15 3 library functions
16 4 special files
16 4 special files
17 5 file formats
17 5 file formats
18 6 games
18 6 games
19 7 miscellaneous
19 7 miscellaneous
20 8 system administration
20 8 system administration
21
21
22 Man pages are written *troff*, a text file formatting system.
22 Man pages are written *troff*, a text file formatting system.
23
23
24 See http://www.tldp.org/HOWTO/Man-Page for a start.
24 See http://www.tldp.org/HOWTO/Man-Page for a start.
25
25
26 Man pages have no subsection only parts.
26 Man pages have no subsection only parts.
27 Standard parts
27 Standard parts
28
28
29 NAME ,
29 NAME ,
30 SYNOPSIS ,
30 SYNOPSIS ,
31 DESCRIPTION ,
31 DESCRIPTION ,
32 OPTIONS ,
32 OPTIONS ,
33 FILES ,
33 FILES ,
34 SEE ALSO ,
34 SEE ALSO ,
35 BUGS ,
35 BUGS ,
36
36
37 and
37 and
38
38
39 AUTHOR .
39 AUTHOR .
40
40
41 A unix-like system keeps an index of the DESCRIPTIONs, which is accesable
41 A unix-like system keeps an index of the DESCRIPTIONs, which is accesable
42 by the command whatis or apropos.
42 by the command whatis or apropos.
43
43
44 """
44 """
45
45
46 __docformat__ = 'reStructuredText'
46 __docformat__ = 'reStructuredText'
47
47
48 import sys
48 import sys
49 import os
49 import os
50 import time
50 import time
51 import re
51 import re
52 from types import ListType
52 from types import ListType
53
53
54 import docutils
54 import docutils
55 from docutils import nodes, utils, writers, languages
55 from docutils import nodes, utils, writers, languages
56 import roman
56 import roman
57
57
58 FIELD_LIST_INDENT = 7
58 FIELD_LIST_INDENT = 7
59 DEFINITION_LIST_INDENT = 7
59 DEFINITION_LIST_INDENT = 7
60 OPTION_LIST_INDENT = 7
60 OPTION_LIST_INDENT = 7
61 BLOCKQOUTE_INDENT = 3.5
61 BLOCKQOUTE_INDENT = 3.5
62
62
63 # Define two macros so man/roff can calculate the
63 # Define two macros so man/roff can calculate the
64 # indent/unindent margins by itself
64 # indent/unindent margins by itself
65 MACRO_DEF = (r""".
65 MACRO_DEF = (r""".
66 .nr rst2man-indent-level 0
66 .nr rst2man-indent-level 0
67 .
67 .
68 .de1 rstReportMargin
68 .de1 rstReportMargin
69 \\$1 \\n[an-margin]
69 \\$1 \\n[an-margin]
70 level \\n[rst2man-indent-level]
70 level \\n[rst2man-indent-level]
71 level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
71 level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
72 -
72 -
73 \\n[rst2man-indent0]
73 \\n[rst2man-indent0]
74 \\n[rst2man-indent1]
74 \\n[rst2man-indent1]
75 \\n[rst2man-indent2]
75 \\n[rst2man-indent2]
76 ..
76 ..
77 .de1 INDENT
77 .de1 INDENT
78 .\" .rstReportMargin pre:
78 .\" .rstReportMargin pre:
79 . RS \\$1
79 . RS \\$1
80 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
80 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
81 . nr rst2man-indent-level +1
81 . nr rst2man-indent-level +1
82 .\" .rstReportMargin post:
82 .\" .rstReportMargin post:
83 ..
83 ..
84 .de UNINDENT
84 .de UNINDENT
85 . RE
85 . RE
86 .\" indent \\n[an-margin]
86 .\" indent \\n[an-margin]
87 .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
87 .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
88 .nr rst2man-indent-level -1
88 .nr rst2man-indent-level -1
89 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
89 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
90 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
90 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
91 ..
91 ..
92 """)
92 """)
93
93
94 class Writer(writers.Writer):
94 class Writer(writers.Writer):
95
95
96 supported = ('manpage')
96 supported = ('manpage')
97 """Formats this writer supports."""
97 """Formats this writer supports."""
98
98
99 output = None
99 output = None
100 """Final translated form of `document`."""
100 """Final translated form of `document`."""
101
101
102 def __init__(self):
102 def __init__(self):
103 writers.Writer.__init__(self)
103 writers.Writer.__init__(self)
104 self.translator_class = Translator
104 self.translator_class = Translator
105
105
106 def translate(self):
106 def translate(self):
107 visitor = self.translator_class(self.document)
107 visitor = self.translator_class(self.document)
108 self.document.walkabout(visitor)
108 self.document.walkabout(visitor)
109 self.output = visitor.astext()
109 self.output = visitor.astext()
110
110
111
111
112 class Table:
112 class Table:
113 def __init__(self):
113 def __init__(self):
114 self._rows = []
114 self._rows = []
115 self._options = ['center', ]
115 self._options = ['center', ]
116 self._tab_char = '\t'
116 self._tab_char = '\t'
117 self._coldefs = []
117 self._coldefs = []
118 def new_row(self):
118 def new_row(self):
119 self._rows.append([])
119 self._rows.append([])
120 def append_separator(self, separator):
120 def append_separator(self, separator):
121 """Append the separator for table head."""
121 """Append the separator for table head."""
122 self._rows.append([separator])
122 self._rows.append([separator])
123 def append_cell(self, cell_lines):
123 def append_cell(self, cell_lines):
124 """cell_lines is an array of lines"""
124 """cell_lines is an array of lines"""
125 start = 0
125 start = 0
126 if len(cell_lines)>0 and cell_lines[0] == '.sp\n':
126 if len(cell_lines)>0 and cell_lines[0] == '.sp\n':
127 start = 1
127 start = 1
128 self._rows[-1].append(cell_lines[start:])
128 self._rows[-1].append(cell_lines[start:])
129 if len(self._coldefs) < len(self._rows[-1]):
129 if len(self._coldefs) < len(self._rows[-1]):
130 self._coldefs.append('l')
130 self._coldefs.append('l')
131 def _minimize_cell(self, cell_lines):
131 def _minimize_cell(self, cell_lines):
132 """Remove leading and trailing blank and ``.sp`` lines"""
132 """Remove leading and trailing blank and ``.sp`` lines"""
133 while (cell_lines and cell_lines[0] in ('\n', '.sp\n')):
133 while (cell_lines and cell_lines[0] in ('\n', '.sp\n')):
134 del cell_lines[0]
134 del cell_lines[0]
135 while (cell_lines and cell_lines[-1] in ('\n', '.sp\n')):
135 while (cell_lines and cell_lines[-1] in ('\n', '.sp\n')):
136 del cell_lines[-1]
136 del cell_lines[-1]
137 def as_list(self):
137 def as_list(self):
138 text = ['.TS\n']
138 text = ['.TS\n']
139 text.append(' '.join(self._options) + ';\n')
139 text.append(' '.join(self._options) + ';\n')
140 text.append('|%s|.\n' % ('|'.join(self._coldefs)))
140 text.append('|%s|.\n' % ('|'.join(self._coldefs)))
141 for row in self._rows:
141 for row in self._rows:
142 # row = array of cells. cell = array of lines.
142 # row = array of cells. cell = array of lines.
143 text.append('_\n') # line above
143 text.append('_\n') # line above
144 text.append('T{\n')
144 text.append('T{\n')
145 for i in range(len(row)):
145 for i in range(len(row)):
146 cell = row[i]
146 cell = row[i]
147 self._minimize_cell(cell)
147 self._minimize_cell(cell)
148 text.extend(cell)
148 text.extend(cell)
149 if not text[-1].endswith('\n'):
149 if not text[-1].endswith('\n'):
150 text[-1] += '\n'
150 text[-1] += '\n'
151 if i < len(row)-1:
151 if i < len(row)-1:
152 text.append('T}'+self._tab_char+'T{\n')
152 text.append('T}'+self._tab_char+'T{\n')
153 else:
153 else:
154 text.append('T}\n')
154 text.append('T}\n')
155 text.append('_\n')
155 text.append('_\n')
156 text.append('.TE\n')
156 text.append('.TE\n')
157 return text
157 return text
158
158
159 class Translator(nodes.NodeVisitor):
159 class Translator(nodes.NodeVisitor):
160 """"""
160 """"""
161
161
162 words_and_spaces = re.compile(r'\S+| +|\n')
162 words_and_spaces = re.compile(r'\S+| +|\n')
163 document_start = """Man page generated from reStructeredText."""
163 document_start = """Man page generated from reStructeredText."""
164
164
165 def __init__(self, document):
165 def __init__(self, document):
166 nodes.NodeVisitor.__init__(self, document)
166 nodes.NodeVisitor.__init__(self, document)
167 self.settings = settings = document.settings
167 self.settings = settings = document.settings
168 lcode = settings.language_code
168 lcode = settings.language_code
169 self.language = languages.get_language(lcode)
169 self.language = languages.get_language(lcode)
170 self.head = []
170 self.head = []
171 self.body = []
171 self.body = []
172 self.foot = []
172 self.foot = []
173 self.section_level = 0
173 self.section_level = 0
174 self.context = []
174 self.context = []
175 self.topic_class = ''
175 self.topic_class = ''
176 self.colspecs = []
176 self.colspecs = []
177 self.compact_p = 1
177 self.compact_p = 1
178 self.compact_simple = None
178 self.compact_simple = None
179 # the list style "*" bullet or "#" numbered
179 # the list style "*" bullet or "#" numbered
180 self._list_char = []
180 self._list_char = []
181 # writing the header .TH and .SH NAME is postboned after
181 # writing the header .TH and .SH NAME is postboned after
182 # docinfo.
182 # docinfo.
183 self._docinfo = {
183 self._docinfo = {
184 "title" : "", "title_upper": "",
184 "title" : "", "title_upper": "",
185 "subtitle" : "",
185 "subtitle" : "",
186 "manual_section" : "", "manual_group" : "",
186 "manual_section" : "", "manual_group" : "",
187 "author" : [],
187 "author" : [],
188 "date" : "",
188 "date" : "",
189 "copyright" : "",
189 "copyright" : "",
190 "version" : "",
190 "version" : "",
191 }
191 }
192 self._docinfo_keys = [] # a list to keep the sequence as in source.
192 self._docinfo_keys = [] # a list to keep the sequence as in source.
193 self._docinfo_names = {} # to get name from text not normalized.
193 self._docinfo_names = {} # to get name from text not normalized.
194 self._in_docinfo = None
194 self._in_docinfo = None
195 self._active_table = None
195 self._active_table = None
196 self._in_literal = False
196 self._in_literal = False
197 self.header_written = 0
197 self.header_written = 0
198 self._line_block = 0
198 self._line_block = 0
199 self.authors = []
199 self.authors = []
200 self.section_level = 0
200 self.section_level = 0
201 self._indent = [0]
201 self._indent = [0]
202 # central definition of simple processing rules
202 # central definition of simple processing rules
203 # what to output on : visit, depart
203 # what to output on : visit, depart
204 # Do not use paragraph requests ``.PP`` because these set indentation.
204 # Do not use paragraph requests ``.PP`` because these set indentation.
205 # use ``.sp``. Remove superfluous ``.sp`` in ``astext``.
205 # use ``.sp``. Remove superfluous ``.sp`` in ``astext``.
206 #
206 #
207 # Fonts are put on a stack, the top one is used.
207 # Fonts are put on a stack, the top one is used.
208 # ``.ft P`` or ``\\fP`` pop from stack.
208 # ``.ft P`` or ``\\fP`` pop from stack.
209 # ``B`` bold, ``I`` italic, ``R`` roman should be available.
209 # ``B`` bold, ``I`` italic, ``R`` roman should be available.
210 # Hopefully ``C`` courier too.
210 # Hopefully ``C`` courier too.
211 self.defs = {
211 self.defs = {
212 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'),
212 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'),
213 'definition_list_item' : ('.TP', ''),
213 'definition_list_item' : ('.TP', ''),
214 'field_name' : ('.TP\n.B ', '\n'),
214 'field_name' : ('.TP\n.B ', '\n'),
215 'literal' : ('\\fC', '\\fP'),
215 'literal' : ('\\fC', '\\fP'),
216 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'),
216 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'),
217
217
218 'option_list_item' : ('.TP\n', ''),
218 'option_list_item' : ('.TP\n', ''),
219
219
220 'reference' : (r'\fI\%', r'\fP'),
220 'reference' : (r'\%', r'\:'),
221 'emphasis': ('\\fI', '\\fP'),
221 'emphasis': ('\\fI', '\\fP'),
222 'strong' : ('\\fB', '\\fP'),
222 'strong' : ('\\fB', '\\fP'),
223 'term' : ('\n.B ', '\n'),
223 'term' : ('\n.B ', '\n'),
224 'title_reference' : ('\\fI', '\\fP'),
224 'title_reference' : ('\\fI', '\\fP'),
225
225
226 'topic-title' : ('.SS ', ),
226 'topic-title' : ('.SS ', ),
227 'sidebar-title' : ('.SS ', ),
227 'sidebar-title' : ('.SS ', ),
228
228
229 'problematic' : ('\n.nf\n', '\n.fi\n'),
229 'problematic' : ('\n.nf\n', '\n.fi\n'),
230 }
230 }
231 # NOTE dont specify the newline before a dot-command, but ensure
231 # NOTE dont specify the newline before a dot-command, but ensure
232 # it is there.
232 # it is there.
233
233
234 def comment_begin(self, text):
234 def comment_begin(self, text):
235 """Return commented version of the passed text WITHOUT end of
235 """Return commented version of the passed text WITHOUT end of
236 line/comment."""
236 line/comment."""
237 prefix = '.\\" '
237 prefix = '.\\" '
238 out_text = ''.join(
238 out_text = ''.join(
239 [(prefix + in_line + '\n')
239 [(prefix + in_line + '\n')
240 for in_line in text.split('\n')])
240 for in_line in text.split('\n')])
241 return out_text
241 return out_text
242
242
243 def comment(self, text):
243 def comment(self, text):
244 """Return commented version of the passed text."""
244 """Return commented version of the passed text."""
245 return self.comment_begin(text)+'.\n'
245 return self.comment_begin(text)+'.\n'
246
246
247 def ensure_eol(self):
247 def ensure_eol(self):
248 """Ensure the last line in body is terminated by new line."""
248 """Ensure the last line in body is terminated by new line."""
249 if self.body[-1][-1] != '\n':
249 if self.body[-1][-1] != '\n':
250 self.body.append('\n')
250 self.body.append('\n')
251
251
252 def astext(self):
252 def astext(self):
253 """Return the final formatted document as a string."""
253 """Return the final formatted document as a string."""
254 if not self.header_written:
254 if not self.header_written:
255 # ensure we get a ".TH" as viewers require it.
255 # ensure we get a ".TH" as viewers require it.
256 self.head.append(self.header())
256 self.head.append(self.header())
257 # filter body
257 # filter body
258 for i in xrange(len(self.body)-1,0,-1):
258 for i in xrange(len(self.body)-1,0,-1):
259 # remove superfluous vertical gaps.
259 # remove superfluous vertical gaps.
260 if self.body[i] == '.sp\n':
260 if self.body[i] == '.sp\n':
261 if self.body[i-1][:4] in ('.BI ','.IP '):
261 if self.body[i-1][:4] in ('.BI ','.IP '):
262 self.body[i] = '.\n'
262 self.body[i] = '.\n'
263 elif (self.body[i-1][:3] == '.B ' and
263 elif (self.body[i-1][:3] == '.B ' and
264 self.body[i-2][:4] == '.TP\n'):
264 self.body[i-2][:4] == '.TP\n'):
265 self.body[i] = '.\n'
265 self.body[i] = '.\n'
266 elif (self.body[i-1] == '\n' and
266 elif (self.body[i-1] == '\n' and
267 self.body[i-2][0] != '.' and
267 self.body[i-2][0] != '.' and
268 (self.body[i-3][:7] == '.TP\n.B '
268 (self.body[i-3][:7] == '.TP\n.B '
269 or self.body[i-3][:4] == '\n.B ')
269 or self.body[i-3][:4] == '\n.B ')
270 ):
270 ):
271 self.body[i] = '.\n'
271 self.body[i] = '.\n'
272 return ''.join(self.head + self.body + self.foot)
272 return ''.join(self.head + self.body + self.foot)
273
273
274 def deunicode(self, text):
274 def deunicode(self, text):
275 text = text.replace(u'\xa0', '\\ ')
275 text = text.replace(u'\xa0', '\\ ')
276 text = text.replace(u'\u2020', '\\(dg')
276 text = text.replace(u'\u2020', '\\(dg')
277 return text
277 return text
278
278
279 def visit_Text(self, node):
279 def visit_Text(self, node):
280 text = node.astext()
280 text = node.astext()
281 text = text.replace('\\','\\e')
281 text = text.replace('\\','\\e')
282 replace_pairs = [
282 replace_pairs = [
283 (u'-', ur'\-'),
283 (u'-', ur'\-'),
284 (u'\'', ur'\(aq'),
284 (u'\'', ur'\(aq'),
285 (u'Β΄', ur'\''),
285 (u'Β΄', ur'\''),
286 (u'`', ur'\(ga'),
286 (u'`', ur'\(ga'),
287 ]
287 ]
288 for (in_char, out_markup) in replace_pairs:
288 for (in_char, out_markup) in replace_pairs:
289 text = text.replace(in_char, out_markup)
289 text = text.replace(in_char, out_markup)
290 # unicode
290 # unicode
291 text = self.deunicode(text)
291 text = self.deunicode(text)
292 if self._in_literal:
292 if self._in_literal:
293 # prevent interpretation of "." at line start
293 # prevent interpretation of "." at line start
294 if text[0] == '.':
294 if text[0] == '.':
295 text = '\\&' + text
295 text = '\\&' + text
296 text = text.replace('\n.', '\n\\&.')
296 text = text.replace('\n.', '\n\\&.')
297 self.body.append(text)
297 self.body.append(text)
298
298
299 def depart_Text(self, node):
299 def depart_Text(self, node):
300 pass
300 pass
301
301
302 def list_start(self, node):
302 def list_start(self, node):
303 class enum_char:
303 class enum_char:
304 enum_style = {
304 enum_style = {
305 'bullet' : '\\(bu',
305 'bullet' : '\\(bu',
306 'emdash' : '\\(em',
306 'emdash' : '\\(em',
307 }
307 }
308
308
309 def __init__(self, style):
309 def __init__(self, style):
310 self._style = style
310 self._style = style
311 if node.has_key('start'):
311 if node.has_key('start'):
312 self._cnt = node['start'] - 1
312 self._cnt = node['start'] - 1
313 else:
313 else:
314 self._cnt = 0
314 self._cnt = 0
315 self._indent = 2
315 self._indent = 2
316 if style == 'arabic':
316 if style == 'arabic':
317 # indentation depends on number of childrens
317 # indentation depends on number of childrens
318 # and start value.
318 # and start value.
319 self._indent = len(str(len(node.children)))
319 self._indent = len(str(len(node.children)))
320 self._indent += len(str(self._cnt)) + 1
320 self._indent += len(str(self._cnt)) + 1
321 elif style == 'loweralpha':
321 elif style == 'loweralpha':
322 self._cnt += ord('a') - 1
322 self._cnt += ord('a') - 1
323 self._indent = 3
323 self._indent = 3
324 elif style == 'upperalpha':
324 elif style == 'upperalpha':
325 self._cnt += ord('A') - 1
325 self._cnt += ord('A') - 1
326 self._indent = 3
326 self._indent = 3
327 elif style.endswith('roman'):
327 elif style.endswith('roman'):
328 self._indent = 5
328 self._indent = 5
329
329
330 def next(self):
330 def next(self):
331 if self._style == 'bullet':
331 if self._style == 'bullet':
332 return self.enum_style[self._style]
332 return self.enum_style[self._style]
333 elif self._style == 'emdash':
333 elif self._style == 'emdash':
334 return self.enum_style[self._style]
334 return self.enum_style[self._style]
335 self._cnt += 1
335 self._cnt += 1
336 # TODO add prefix postfix
336 # TODO add prefix postfix
337 if self._style == 'arabic':
337 if self._style == 'arabic':
338 return "%d." % self._cnt
338 return "%d." % self._cnt
339 elif self._style in ('loweralpha', 'upperalpha'):
339 elif self._style in ('loweralpha', 'upperalpha'):
340 return "%c." % self._cnt
340 return "%c." % self._cnt
341 elif self._style.endswith('roman'):
341 elif self._style.endswith('roman'):
342 res = roman.toRoman(self._cnt) + '.'
342 res = roman.toRoman(self._cnt) + '.'
343 if self._style.startswith('upper'):
343 if self._style.startswith('upper'):
344 return res.upper()
344 return res.upper()
345 return res.lower()
345 return res.lower()
346 else:
346 else:
347 return "%d." % self._cnt
347 return "%d." % self._cnt
348 def get_width(self):
348 def get_width(self):
349 return self._indent
349 return self._indent
350 def __repr__(self):
350 def __repr__(self):
351 return 'enum_style-%s' % list(self._style)
351 return 'enum_style-%s' % list(self._style)
352
352
353 if node.has_key('enumtype'):
353 if node.has_key('enumtype'):
354 self._list_char.append(enum_char(node['enumtype']))
354 self._list_char.append(enum_char(node['enumtype']))
355 else:
355 else:
356 self._list_char.append(enum_char('bullet'))
356 self._list_char.append(enum_char('bullet'))
357 if len(self._list_char) > 1:
357 if len(self._list_char) > 1:
358 # indent nested lists
358 # indent nested lists
359 self.indent(self._list_char[-2].get_width())
359 self.indent(self._list_char[-2].get_width())
360 else:
360 else:
361 self.indent(self._list_char[-1].get_width())
361 self.indent(self._list_char[-1].get_width())
362
362
363 def list_end(self):
363 def list_end(self):
364 self.dedent()
364 self.dedent()
365 self._list_char.pop()
365 self._list_char.pop()
366
366
367 def header(self):
367 def header(self):
368 tmpl = (".TH %(title_upper)s %(manual_section)s"
368 tmpl = (".TH %(title_upper)s %(manual_section)s"
369 " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n"
369 " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n"
370 ".SH NAME\n"
370 ".SH NAME\n"
371 "%(title)s \- %(subtitle)s\n")
371 "%(title)s \- %(subtitle)s\n")
372 return tmpl % self._docinfo
372 return tmpl % self._docinfo
373
373
374 def append_header(self):
374 def append_header(self):
375 """append header with .TH and .SH NAME"""
375 """append header with .TH and .SH NAME"""
376 # NOTE before everything
376 # NOTE before everything
377 # .TH title_upper section date source manual
377 # .TH title_upper section date source manual
378 if self.header_written:
378 if self.header_written:
379 return
379 return
380 self.body.append(self.header())
380 self.body.append(self.header())
381 self.body.append(MACRO_DEF)
381 self.body.append(MACRO_DEF)
382 self.header_written = 1
382 self.header_written = 1
383
383
384 def visit_address(self, node):
384 def visit_address(self, node):
385 self.visit_docinfo_item(node, 'address')
385 self.visit_docinfo_item(node, 'address')
386
386
387 def depart_address(self, node):
387 def depart_address(self, node):
388 pass
388 pass
389
389
390 def visit_admonition(self, node, name=None):
390 def visit_admonition(self, node, name=None):
391 if name:
391 if name:
392 self.body.append('.IP %s\n' %
392 self.body.append('.IP %s\n' %
393 self.language.labels.get(name, name))
393 self.language.labels.get(name, name))
394
394
395 def depart_admonition(self, node):
395 def depart_admonition(self, node):
396 self.body.append('.RE\n')
396 self.body.append('.RE\n')
397
397
398 def visit_attention(self, node):
398 def visit_attention(self, node):
399 self.visit_admonition(node, 'attention')
399 self.visit_admonition(node, 'attention')
400
400
401 depart_attention = depart_admonition
401 depart_attention = depart_admonition
402
402
403 def visit_docinfo_item(self, node, name):
403 def visit_docinfo_item(self, node, name):
404 if name == 'author':
404 if name == 'author':
405 self._docinfo[name].append(node.astext())
405 self._docinfo[name].append(node.astext())
406 else:
406 else:
407 self._docinfo[name] = node.astext()
407 self._docinfo[name] = node.astext()
408 self._docinfo_keys.append(name)
408 self._docinfo_keys.append(name)
409 raise nodes.SkipNode
409 raise nodes.SkipNode
410
410
411 def depart_docinfo_item(self, node):
411 def depart_docinfo_item(self, node):
412 pass
412 pass
413
413
414 def visit_author(self, node):
414 def visit_author(self, node):
415 self.visit_docinfo_item(node, 'author')
415 self.visit_docinfo_item(node, 'author')
416
416
417 depart_author = depart_docinfo_item
417 depart_author = depart_docinfo_item
418
418
419 def visit_authors(self, node):
419 def visit_authors(self, node):
420 # _author is called anyway.
420 # _author is called anyway.
421 pass
421 pass
422
422
423 def depart_authors(self, node):
423 def depart_authors(self, node):
424 pass
424 pass
425
425
426 def visit_block_quote(self, node):
426 def visit_block_quote(self, node):
427 # BUG/HACK: indent alway uses the _last_ indention,
427 # BUG/HACK: indent alway uses the _last_ indention,
428 # thus we need two of them.
428 # thus we need two of them.
429 self.indent(BLOCKQOUTE_INDENT)
429 self.indent(BLOCKQOUTE_INDENT)
430 self.indent(0)
430 self.indent(0)
431
431
432 def depart_block_quote(self, node):
432 def depart_block_quote(self, node):
433 self.dedent()
433 self.dedent()
434 self.dedent()
434 self.dedent()
435
435
436 def visit_bullet_list(self, node):
436 def visit_bullet_list(self, node):
437 self.list_start(node)
437 self.list_start(node)
438
438
439 def depart_bullet_list(self, node):
439 def depart_bullet_list(self, node):
440 self.list_end()
440 self.list_end()
441
441
442 def visit_caption(self, node):
442 def visit_caption(self, node):
443 pass
443 pass
444
444
445 def depart_caption(self, node):
445 def depart_caption(self, node):
446 pass
446 pass
447
447
448 def visit_caution(self, node):
448 def visit_caution(self, node):
449 self.visit_admonition(node, 'caution')
449 self.visit_admonition(node, 'caution')
450
450
451 depart_caution = depart_admonition
451 depart_caution = depart_admonition
452
452
453 def visit_citation(self, node):
453 def visit_citation(self, node):
454 num,text = node.astext().split(None,1)
454 num,text = node.astext().split(None,1)
455 num = num.strip()
455 num = num.strip()
456 self.body.append('.IP [%s] 5\n' % num)
456 self.body.append('.IP [%s] 5\n' % num)
457
457
458 def depart_citation(self, node):
458 def depart_citation(self, node):
459 pass
459 pass
460
460
461 def visit_citation_reference(self, node):
461 def visit_citation_reference(self, node):
462 self.body.append('['+node.astext()+']')
462 self.body.append('['+node.astext()+']')
463 raise nodes.SkipNode
463 raise nodes.SkipNode
464
464
465 def visit_classifier(self, node):
465 def visit_classifier(self, node):
466 pass
466 pass
467
467
468 def depart_classifier(self, node):
468 def depart_classifier(self, node):
469 pass
469 pass
470
470
471 def visit_colspec(self, node):
471 def visit_colspec(self, node):
472 self.colspecs.append(node)
472 self.colspecs.append(node)
473
473
474 def depart_colspec(self, node):
474 def depart_colspec(self, node):
475 pass
475 pass
476
476
477 def write_colspecs(self):
477 def write_colspecs(self):
478 self.body.append("%s.\n" % ('L '*len(self.colspecs)))
478 self.body.append("%s.\n" % ('L '*len(self.colspecs)))
479
479
480 def visit_comment(self, node,
480 def visit_comment(self, node,
481 sub=re.compile('-(?=-)').sub):
481 sub=re.compile('-(?=-)').sub):
482 self.body.append(self.comment(node.astext()))
482 self.body.append(self.comment(node.astext()))
483 raise nodes.SkipNode
483 raise nodes.SkipNode
484
484
485 def visit_contact(self, node):
485 def visit_contact(self, node):
486 self.visit_docinfo_item(node, 'contact')
486 self.visit_docinfo_item(node, 'contact')
487
487
488 depart_contact = depart_docinfo_item
488 depart_contact = depart_docinfo_item
489
489
490 def visit_container(self, node):
490 def visit_container(self, node):
491 pass
491 pass
492
492
493 def depart_container(self, node):
493 def depart_container(self, node):
494 pass
494 pass
495
495
496 def visit_compound(self, node):
496 def visit_compound(self, node):
497 pass
497 pass
498
498
499 def depart_compound(self, node):
499 def depart_compound(self, node):
500 pass
500 pass
501
501
502 def visit_copyright(self, node):
502 def visit_copyright(self, node):
503 self.visit_docinfo_item(node, 'copyright')
503 self.visit_docinfo_item(node, 'copyright')
504
504
505 def visit_danger(self, node):
505 def visit_danger(self, node):
506 self.visit_admonition(node, 'danger')
506 self.visit_admonition(node, 'danger')
507
507
508 depart_danger = depart_admonition
508 depart_danger = depart_admonition
509
509
510 def visit_date(self, node):
510 def visit_date(self, node):
511 self.visit_docinfo_item(node, 'date')
511 self.visit_docinfo_item(node, 'date')
512
512
513 def visit_decoration(self, node):
513 def visit_decoration(self, node):
514 pass
514 pass
515
515
516 def depart_decoration(self, node):
516 def depart_decoration(self, node):
517 pass
517 pass
518
518
519 def visit_definition(self, node):
519 def visit_definition(self, node):
520 pass
520 pass
521
521
522 def depart_definition(self, node):
522 def depart_definition(self, node):
523 pass
523 pass
524
524
525 def visit_definition_list(self, node):
525 def visit_definition_list(self, node):
526 self.indent(DEFINITION_LIST_INDENT)
526 self.indent(DEFINITION_LIST_INDENT)
527
527
528 def depart_definition_list(self, node):
528 def depart_definition_list(self, node):
529 self.dedent()
529 self.dedent()
530
530
531 def visit_definition_list_item(self, node):
531 def visit_definition_list_item(self, node):
532 self.body.append(self.defs['definition_list_item'][0])
532 self.body.append(self.defs['definition_list_item'][0])
533
533
534 def depart_definition_list_item(self, node):
534 def depart_definition_list_item(self, node):
535 self.body.append(self.defs['definition_list_item'][1])
535 self.body.append(self.defs['definition_list_item'][1])
536
536
537 def visit_description(self, node):
537 def visit_description(self, node):
538 pass
538 pass
539
539
540 def depart_description(self, node):
540 def depart_description(self, node):
541 pass
541 pass
542
542
543 def visit_docinfo(self, node):
543 def visit_docinfo(self, node):
544 self._in_docinfo = 1
544 self._in_docinfo = 1
545
545
546 def depart_docinfo(self, node):
546 def depart_docinfo(self, node):
547 self._in_docinfo = None
547 self._in_docinfo = None
548 # NOTE nothing should be written before this
548 # NOTE nothing should be written before this
549 self.append_header()
549 self.append_header()
550
550
551 def visit_doctest_block(self, node):
551 def visit_doctest_block(self, node):
552 self.body.append(self.defs['literal_block'][0])
552 self.body.append(self.defs['literal_block'][0])
553 self._in_literal = True
553 self._in_literal = True
554
554
555 def depart_doctest_block(self, node):
555 def depart_doctest_block(self, node):
556 self._in_literal = False
556 self._in_literal = False
557 self.body.append(self.defs['literal_block'][1])
557 self.body.append(self.defs['literal_block'][1])
558
558
559 def visit_document(self, node):
559 def visit_document(self, node):
560 # no blank line between comment and header.
560 # no blank line between comment and header.
561 self.body.append(self.comment(self.document_start).rstrip()+'\n')
561 self.body.append(self.comment(self.document_start).rstrip()+'\n')
562 # writing header is postboned
562 # writing header is postboned
563 self.header_written = 0
563 self.header_written = 0
564
564
565 def depart_document(self, node):
565 def depart_document(self, node):
566 if self._docinfo['author']:
566 if self._docinfo['author']:
567 self.body.append('.SH AUTHOR\n%s\n'
567 self.body.append('.SH AUTHOR\n%s\n'
568 % ', '.join(self._docinfo['author']))
568 % ', '.join(self._docinfo['author']))
569 skip = ('author', 'copyright', 'date',
569 skip = ('author', 'copyright', 'date',
570 'manual_group', 'manual_section',
570 'manual_group', 'manual_section',
571 'subtitle',
571 'subtitle',
572 'title', 'title_upper', 'version')
572 'title', 'title_upper', 'version')
573 for name in self._docinfo_keys:
573 for name in self._docinfo_keys:
574 if name == 'address':
574 if name == 'address':
575 self.body.append("\n%s:\n%s%s.nf\n%s\n.fi\n%s%s" % (
575 self.body.append("\n%s:\n%s%s.nf\n%s\n.fi\n%s%s" % (
576 self.language.labels.get(name, name),
576 self.language.labels.get(name, name),
577 self.defs['indent'][0] % 0,
577 self.defs['indent'][0] % 0,
578 self.defs['indent'][0] % BLOCKQOUTE_INDENT,
578 self.defs['indent'][0] % BLOCKQOUTE_INDENT,
579 self._docinfo[name],
579 self._docinfo[name],
580 self.defs['indent'][1],
580 self.defs['indent'][1],
581 self.defs['indent'][1],
581 self.defs['indent'][1],
582 ) )
582 ) )
583 elif not name in skip:
583 elif not name in skip:
584 if name in self._docinfo_names:
584 if name in self._docinfo_names:
585 label = self._docinfo_names[name]
585 label = self._docinfo_names[name]
586 else:
586 else:
587 label = self.language.labels.get(name, name)
587 label = self.language.labels.get(name, name)
588 self.body.append("\n%s: %s\n" % (label, self._docinfo[name]) )
588 self.body.append("\n%s: %s\n" % (label, self._docinfo[name]) )
589 if self._docinfo['copyright']:
589 if self._docinfo['copyright']:
590 self.body.append('.SH COPYRIGHT\n%s\n'
590 self.body.append('.SH COPYRIGHT\n%s\n'
591 % self._docinfo['copyright'])
591 % self._docinfo['copyright'])
592 self.body.append( self.comment(
592 self.body.append( self.comment(
593 'Generated by docutils manpage writer.\n' ) )
593 'Generated by docutils manpage writer.\n' ) )
594
594
595 def visit_emphasis(self, node):
595 def visit_emphasis(self, node):
596 self.body.append(self.defs['emphasis'][0])
596 self.body.append(self.defs['emphasis'][0])
597
597
598 def depart_emphasis(self, node):
598 def depart_emphasis(self, node):
599 self.body.append(self.defs['emphasis'][1])
599 self.body.append(self.defs['emphasis'][1])
600
600
601 def visit_entry(self, node):
601 def visit_entry(self, node):
602 # a cell in a table row
602 # a cell in a table row
603 if 'morerows' in node:
603 if 'morerows' in node:
604 self.document.reporter.warning('"table row spanning" not supported',
604 self.document.reporter.warning('"table row spanning" not supported',
605 base_node=node)
605 base_node=node)
606 if 'morecols' in node:
606 if 'morecols' in node:
607 self.document.reporter.warning(
607 self.document.reporter.warning(
608 '"table cell spanning" not supported', base_node=node)
608 '"table cell spanning" not supported', base_node=node)
609 self.context.append(len(self.body))
609 self.context.append(len(self.body))
610
610
611 def depart_entry(self, node):
611 def depart_entry(self, node):
612 start = self.context.pop()
612 start = self.context.pop()
613 self._active_table.append_cell(self.body[start:])
613 self._active_table.append_cell(self.body[start:])
614 del self.body[start:]
614 del self.body[start:]
615
615
616 def visit_enumerated_list(self, node):
616 def visit_enumerated_list(self, node):
617 self.list_start(node)
617 self.list_start(node)
618
618
619 def depart_enumerated_list(self, node):
619 def depart_enumerated_list(self, node):
620 self.list_end()
620 self.list_end()
621
621
622 def visit_error(self, node):
622 def visit_error(self, node):
623 self.visit_admonition(node, 'error')
623 self.visit_admonition(node, 'error')
624
624
625 depart_error = depart_admonition
625 depart_error = depart_admonition
626
626
627 def visit_field(self, node):
627 def visit_field(self, node):
628 pass
628 pass
629
629
630 def depart_field(self, node):
630 def depart_field(self, node):
631 pass
631 pass
632
632
633 def visit_field_body(self, node):
633 def visit_field_body(self, node):
634 if self._in_docinfo:
634 if self._in_docinfo:
635 name_normalized = self._field_name.lower().replace(" ","_")
635 name_normalized = self._field_name.lower().replace(" ","_")
636 self._docinfo_names[name_normalized] = self._field_name
636 self._docinfo_names[name_normalized] = self._field_name
637 self.visit_docinfo_item(node, name_normalized)
637 self.visit_docinfo_item(node, name_normalized)
638 raise nodes.SkipNode
638 raise nodes.SkipNode
639
639
640 def depart_field_body(self, node):
640 def depart_field_body(self, node):
641 pass
641 pass
642
642
643 def visit_field_list(self, node):
643 def visit_field_list(self, node):
644 self.indent(FIELD_LIST_INDENT)
644 self.indent(FIELD_LIST_INDENT)
645
645
646 def depart_field_list(self, node):
646 def depart_field_list(self, node):
647 self.dedent()
647 self.dedent()
648
648
649 def visit_field_name(self, node):
649 def visit_field_name(self, node):
650 if self._in_docinfo:
650 if self._in_docinfo:
651 self._field_name = node.astext()
651 self._field_name = node.astext()
652 raise nodes.SkipNode
652 raise nodes.SkipNode
653 else:
653 else:
654 self.body.append(self.defs['field_name'][0])
654 self.body.append(self.defs['field_name'][0])
655
655
656 def depart_field_name(self, node):
656 def depart_field_name(self, node):
657 self.body.append(self.defs['field_name'][1])
657 self.body.append(self.defs['field_name'][1])
658
658
659 def visit_figure(self, node):
659 def visit_figure(self, node):
660 self.indent(2.5)
660 self.indent(2.5)
661 self.indent(0)
661 self.indent(0)
662
662
663 def depart_figure(self, node):
663 def depart_figure(self, node):
664 self.dedent()
664 self.dedent()
665 self.dedent()
665 self.dedent()
666
666
667 def visit_footer(self, node):
667 def visit_footer(self, node):
668 self.document.reporter.warning('"footer" not supported',
668 self.document.reporter.warning('"footer" not supported',
669 base_node=node)
669 base_node=node)
670
670
671 def depart_footer(self, node):
671 def depart_footer(self, node):
672 pass
672 pass
673
673
674 def visit_footnote(self, node):
674 def visit_footnote(self, node):
675 num,text = node.astext().split(None,1)
675 num,text = node.astext().split(None,1)
676 num = num.strip()
676 num = num.strip()
677 self.body.append('.IP [%s] 5\n' % self.deunicode(num))
677 self.body.append('.IP [%s] 5\n' % self.deunicode(num))
678
678
679 def depart_footnote(self, node):
679 def depart_footnote(self, node):
680 pass
680 pass
681
681
682 def footnote_backrefs(self, node):
682 def footnote_backrefs(self, node):
683 self.document.reporter.warning('"footnote_backrefs" not supported',
683 self.document.reporter.warning('"footnote_backrefs" not supported',
684 base_node=node)
684 base_node=node)
685
685
686 def visit_footnote_reference(self, node):
686 def visit_footnote_reference(self, node):
687 self.body.append('['+self.deunicode(node.astext())+']')
687 self.body.append('['+self.deunicode(node.astext())+']')
688 raise nodes.SkipNode
688 raise nodes.SkipNode
689
689
690 def depart_footnote_reference(self, node):
690 def depart_footnote_reference(self, node):
691 pass
691 pass
692
692
693 def visit_generated(self, node):
693 def visit_generated(self, node):
694 pass
694 pass
695
695
696 def depart_generated(self, node):
696 def depart_generated(self, node):
697 pass
697 pass
698
698
699 def visit_header(self, node):
699 def visit_header(self, node):
700 raise NotImplementedError, node.astext()
700 raise NotImplementedError, node.astext()
701
701
702 def depart_header(self, node):
702 def depart_header(self, node):
703 pass
703 pass
704
704
705 def visit_hint(self, node):
705 def visit_hint(self, node):
706 self.visit_admonition(node, 'hint')
706 self.visit_admonition(node, 'hint')
707
707
708 depart_hint = depart_admonition
708 depart_hint = depart_admonition
709
709
710 def visit_subscript(self, node):
710 def visit_subscript(self, node):
711 self.body.append('\\s-2\\d')
711 self.body.append('\\s-2\\d')
712
712
713 def depart_subscript(self, node):
713 def depart_subscript(self, node):
714 self.body.append('\\u\\s0')
714 self.body.append('\\u\\s0')
715
715
716 def visit_superscript(self, node):
716 def visit_superscript(self, node):
717 self.body.append('\\s-2\\u')
717 self.body.append('\\s-2\\u')
718
718
719 def depart_superscript(self, node):
719 def depart_superscript(self, node):
720 self.body.append('\\d\\s0')
720 self.body.append('\\d\\s0')
721
721
722 def visit_attribution(self, node):
722 def visit_attribution(self, node):
723 self.body.append('\\(em ')
723 self.body.append('\\(em ')
724
724
725 def depart_attribution(self, node):
725 def depart_attribution(self, node):
726 self.body.append('\n')
726 self.body.append('\n')
727
727
728 def visit_image(self, node):
728 def visit_image(self, node):
729 self.document.reporter.warning('"image" not supported',
729 self.document.reporter.warning('"image" not supported',
730 base_node=node)
730 base_node=node)
731 text = []
731 text = []
732 if 'alt' in node.attributes:
732 if 'alt' in node.attributes:
733 text.append(node.attributes['alt'])
733 text.append(node.attributes['alt'])
734 if 'uri' in node.attributes:
734 if 'uri' in node.attributes:
735 text.append(node.attributes['uri'])
735 text.append(node.attributes['uri'])
736 self.body.append('[image: %s]\n' % ('/'.join(text)))
736 self.body.append('[image: %s]\n' % ('/'.join(text)))
737 raise nodes.SkipNode
737 raise nodes.SkipNode
738
738
739 def visit_important(self, node):
739 def visit_important(self, node):
740 self.visit_admonition(node, 'important')
740 self.visit_admonition(node, 'important')
741
741
742 depart_important = depart_admonition
742 depart_important = depart_admonition
743
743
744 def visit_label(self, node):
744 def visit_label(self, node):
745 # footnote and citation
745 # footnote and citation
746 if (isinstance(node.parent, nodes.footnote)
746 if (isinstance(node.parent, nodes.footnote)
747 or isinstance(node.parent, nodes.citation)):
747 or isinstance(node.parent, nodes.citation)):
748 raise nodes.SkipNode
748 raise nodes.SkipNode
749 self.document.reporter.warning('"unsupported "label"',
749 self.document.reporter.warning('"unsupported "label"',
750 base_node=node)
750 base_node=node)
751 self.body.append('[')
751 self.body.append('[')
752
752
753 def depart_label(self, node):
753 def depart_label(self, node):
754 self.body.append(']\n')
754 self.body.append(']\n')
755
755
756 def visit_legend(self, node):
756 def visit_legend(self, node):
757 pass
757 pass
758
758
759 def depart_legend(self, node):
759 def depart_legend(self, node):
760 pass
760 pass
761
761
762 # WHAT should we use .INDENT, .UNINDENT ?
762 # WHAT should we use .INDENT, .UNINDENT ?
763 def visit_line_block(self, node):
763 def visit_line_block(self, node):
764 self._line_block += 1
764 self._line_block += 1
765 if self._line_block == 1:
765 if self._line_block == 1:
766 self.body.append('.nf\n')
766 self.body.append('.nf\n')
767 else:
767 else:
768 self.body.append('.in +2\n')
768 self.body.append('.in +2\n')
769
769
770 def depart_line_block(self, node):
770 def depart_line_block(self, node):
771 self._line_block -= 1
771 self._line_block -= 1
772 if self._line_block == 0:
772 if self._line_block == 0:
773 self.body.append('.fi\n')
773 self.body.append('.fi\n')
774 self.body.append('.sp\n')
774 self.body.append('.sp\n')
775 else:
775 else:
776 self.body.append('.in -2\n')
776 self.body.append('.in -2\n')
777
777
778 def visit_line(self, node):
778 def visit_line(self, node):
779 pass
779 pass
780
780
781 def depart_line(self, node):
781 def depart_line(self, node):
782 self.body.append('\n')
782 self.body.append('\n')
783
783
784 def visit_list_item(self, node):
784 def visit_list_item(self, node):
785 # man 7 man argues to use ".IP" instead of ".TP"
785 # man 7 man argues to use ".IP" instead of ".TP"
786 self.body.append('.IP %s %d\n' % (
786 self.body.append('.IP %s %d\n' % (
787 self._list_char[-1].next(),
787 self._list_char[-1].next(),
788 self._list_char[-1].get_width(),) )
788 self._list_char[-1].get_width(),) )
789
789
790 def depart_list_item(self, node):
790 def depart_list_item(self, node):
791 pass
791 pass
792
792
793 def visit_literal(self, node):
793 def visit_literal(self, node):
794 self.body.append(self.defs['literal'][0])
794 self.body.append(self.defs['literal'][0])
795
795
796 def depart_literal(self, node):
796 def depart_literal(self, node):
797 self.body.append(self.defs['literal'][1])
797 self.body.append(self.defs['literal'][1])
798
798
799 def visit_literal_block(self, node):
799 def visit_literal_block(self, node):
800 self.body.append(self.defs['literal_block'][0])
800 self.body.append(self.defs['literal_block'][0])
801 self._in_literal = True
801 self._in_literal = True
802
802
803 def depart_literal_block(self, node):
803 def depart_literal_block(self, node):
804 self._in_literal = False
804 self._in_literal = False
805 self.body.append(self.defs['literal_block'][1])
805 self.body.append(self.defs['literal_block'][1])
806
806
807 def visit_meta(self, node):
807 def visit_meta(self, node):
808 raise NotImplementedError, node.astext()
808 raise NotImplementedError, node.astext()
809
809
810 def depart_meta(self, node):
810 def depart_meta(self, node):
811 pass
811 pass
812
812
813 def visit_note(self, node):
813 def visit_note(self, node):
814 self.visit_admonition(node, 'note')
814 self.visit_admonition(node, 'note')
815
815
816 depart_note = depart_admonition
816 depart_note = depart_admonition
817
817
818 def indent(self, by=0.5):
818 def indent(self, by=0.5):
819 # if we are in a section ".SH" there already is a .RS
819 # if we are in a section ".SH" there already is a .RS
820 step = self._indent[-1]
820 step = self._indent[-1]
821 self._indent.append(by)
821 self._indent.append(by)
822 self.body.append(self.defs['indent'][0] % step)
822 self.body.append(self.defs['indent'][0] % step)
823
823
824 def dedent(self):
824 def dedent(self):
825 self._indent.pop()
825 self._indent.pop()
826 self.body.append(self.defs['indent'][1])
826 self.body.append(self.defs['indent'][1])
827
827
828 def visit_option_list(self, node):
828 def visit_option_list(self, node):
829 self.indent(OPTION_LIST_INDENT)
829 self.indent(OPTION_LIST_INDENT)
830
830
831 def depart_option_list(self, node):
831 def depart_option_list(self, node):
832 self.dedent()
832 self.dedent()
833
833
834 def visit_option_list_item(self, node):
834 def visit_option_list_item(self, node):
835 # one item of the list
835 # one item of the list
836 self.body.append(self.defs['option_list_item'][0])
836 self.body.append(self.defs['option_list_item'][0])
837
837
838 def depart_option_list_item(self, node):
838 def depart_option_list_item(self, node):
839 self.body.append(self.defs['option_list_item'][1])
839 self.body.append(self.defs['option_list_item'][1])
840
840
841 def visit_option_group(self, node):
841 def visit_option_group(self, node):
842 # as one option could have several forms it is a group
842 # as one option could have several forms it is a group
843 # options without parameter bold only, .B, -v
843 # options without parameter bold only, .B, -v
844 # options with parameter bold italic, .BI, -f file
844 # options with parameter bold italic, .BI, -f file
845 #
845 #
846 # we do not know if .B or .BI
846 # we do not know if .B or .BI
847 self.context.append('.B') # blind guess
847 self.context.append('.B') # blind guess
848 self.context.append(len(self.body)) # to be able to insert later
848 self.context.append(len(self.body)) # to be able to insert later
849 self.context.append(0) # option counter
849 self.context.append(0) # option counter
850
850
851 def depart_option_group(self, node):
851 def depart_option_group(self, node):
852 self.context.pop() # the counter
852 self.context.pop() # the counter
853 start_position = self.context.pop()
853 start_position = self.context.pop()
854 text = self.body[start_position:]
854 text = self.body[start_position:]
855 del self.body[start_position:]
855 del self.body[start_position:]
856 self.body.append('%s%s\n' % (self.context.pop(), ''.join(text)))
856 self.body.append('%s%s\n' % (self.context.pop(), ''.join(text)))
857
857
858 def visit_option(self, node):
858 def visit_option(self, node):
859 # each form of the option will be presented separately
859 # each form of the option will be presented separately
860 if self.context[-1]>0:
860 if self.context[-1]>0:
861 self.body.append(', ')
861 self.body.append(', ')
862 if self.context[-3] == '.BI':
862 if self.context[-3] == '.BI':
863 self.body.append('\\')
863 self.body.append('\\')
864 self.body.append(' ')
864 self.body.append(' ')
865
865
866 def depart_option(self, node):
866 def depart_option(self, node):
867 self.context[-1] += 1
867 self.context[-1] += 1
868
868
869 def visit_option_string(self, node):
869 def visit_option_string(self, node):
870 # do not know if .B or .BI
870 # do not know if .B or .BI
871 pass
871 pass
872
872
873 def depart_option_string(self, node):
873 def depart_option_string(self, node):
874 pass
874 pass
875
875
876 def visit_option_argument(self, node):
876 def visit_option_argument(self, node):
877 self.context[-3] = '.BI' # bold/italic alternate
877 self.context[-3] = '.BI' # bold/italic alternate
878 if node['delimiter'] != ' ':
878 if node['delimiter'] != ' ':
879 self.body.append('\\fB%s ' % node['delimiter'] )
879 self.body.append('\\fB%s ' % node['delimiter'] )
880 elif self.body[len(self.body)-1].endswith('='):
880 elif self.body[len(self.body)-1].endswith('='):
881 # a blank only means no blank in output, just changing font
881 # a blank only means no blank in output, just changing font
882 self.body.append(' ')
882 self.body.append(' ')
883 else:
883 else:
884 # blank backslash blank, switch font then a blank
884 # blank backslash blank, switch font then a blank
885 self.body.append(' \\ ')
885 self.body.append(' \\ ')
886
886
887 def depart_option_argument(self, node):
887 def depart_option_argument(self, node):
888 pass
888 pass
889
889
890 def visit_organization(self, node):
890 def visit_organization(self, node):
891 self.visit_docinfo_item(node, 'organization')
891 self.visit_docinfo_item(node, 'organization')
892
892
893 def depart_organization(self, node):
893 def depart_organization(self, node):
894 pass
894 pass
895
895
896 def visit_paragraph(self, node):
896 def visit_paragraph(self, node):
897 # ``.PP`` : Start standard indented paragraph.
897 # ``.PP`` : Start standard indented paragraph.
898 # ``.LP`` : Start block paragraph, all except the first.
898 # ``.LP`` : Start block paragraph, all except the first.
899 # ``.P [type]`` : Start paragraph type.
899 # ``.P [type]`` : Start paragraph type.
900 # NOTE dont use paragraph starts because they reset indentation.
900 # NOTE dont use paragraph starts because they reset indentation.
901 # ``.sp`` is only vertical space
901 # ``.sp`` is only vertical space
902 self.ensure_eol()
902 self.ensure_eol()
903 self.body.append('.sp\n')
903 self.body.append('.sp\n')
904
904
905 def depart_paragraph(self, node):
905 def depart_paragraph(self, node):
906 self.body.append('\n')
906 self.body.append('\n')
907
907
908 def visit_problematic(self, node):
908 def visit_problematic(self, node):
909 self.body.append(self.defs['problematic'][0])
909 self.body.append(self.defs['problematic'][0])
910
910
911 def depart_problematic(self, node):
911 def depart_problematic(self, node):
912 self.body.append(self.defs['problematic'][1])
912 self.body.append(self.defs['problematic'][1])
913
913
914 def visit_raw(self, node):
914 def visit_raw(self, node):
915 if node.get('format') == 'manpage':
915 if node.get('format') == 'manpage':
916 self.body.append(node.astext() + "\n")
916 self.body.append(node.astext() + "\n")
917 # Keep non-manpage raw text out of output:
917 # Keep non-manpage raw text out of output:
918 raise nodes.SkipNode
918 raise nodes.SkipNode
919
919
920 def visit_reference(self, node):
920 def visit_reference(self, node):
921 """E.g. link or email address."""
921 """E.g. link or email address."""
922 self.body.append(self.defs['reference'][0])
922 self.body.append(self.defs['reference'][0])
923
923
924 def depart_reference(self, node):
924 def depart_reference(self, node):
925 self.body.append(self.defs['reference'][1])
925 self.body.append(self.defs['reference'][1])
926
926
927 def visit_revision(self, node):
927 def visit_revision(self, node):
928 self.visit_docinfo_item(node, 'revision')
928 self.visit_docinfo_item(node, 'revision')
929
929
930 depart_revision = depart_docinfo_item
930 depart_revision = depart_docinfo_item
931
931
932 def visit_row(self, node):
932 def visit_row(self, node):
933 self._active_table.new_row()
933 self._active_table.new_row()
934
934
935 def depart_row(self, node):
935 def depart_row(self, node):
936 pass
936 pass
937
937
938 def visit_section(self, node):
938 def visit_section(self, node):
939 self.section_level += 1
939 self.section_level += 1
940
940
941 def depart_section(self, node):
941 def depart_section(self, node):
942 self.section_level -= 1
942 self.section_level -= 1
943
943
944 def visit_status(self, node):
944 def visit_status(self, node):
945 self.visit_docinfo_item(node, 'status')
945 self.visit_docinfo_item(node, 'status')
946
946
947 depart_status = depart_docinfo_item
947 depart_status = depart_docinfo_item
948
948
949 def visit_strong(self, node):
949 def visit_strong(self, node):
950 self.body.append(self.defs['strong'][0])
950 self.body.append(self.defs['strong'][0])
951
951
952 def depart_strong(self, node):
952 def depart_strong(self, node):
953 self.body.append(self.defs['strong'][1])
953 self.body.append(self.defs['strong'][1])
954
954
955 def visit_substitution_definition(self, node):
955 def visit_substitution_definition(self, node):
956 """Internal only."""
956 """Internal only."""
957 raise nodes.SkipNode
957 raise nodes.SkipNode
958
958
959 def visit_substitution_reference(self, node):
959 def visit_substitution_reference(self, node):
960 self.document.reporter.warning('"substitution_reference" not supported',
960 self.document.reporter.warning('"substitution_reference" not supported',
961 base_node=node)
961 base_node=node)
962
962
963 def visit_subtitle(self, node):
963 def visit_subtitle(self, node):
964 if isinstance(node.parent, nodes.sidebar):
964 if isinstance(node.parent, nodes.sidebar):
965 self.body.append(self.defs['strong'][0])
965 self.body.append(self.defs['strong'][0])
966 elif isinstance(node.parent, nodes.document):
966 elif isinstance(node.parent, nodes.document):
967 self.visit_docinfo_item(node, 'subtitle')
967 self.visit_docinfo_item(node, 'subtitle')
968 elif isinstance(node.parent, nodes.section):
968 elif isinstance(node.parent, nodes.section):
969 self.body.append(self.defs['strong'][0])
969 self.body.append(self.defs['strong'][0])
970
970
971 def depart_subtitle(self, node):
971 def depart_subtitle(self, node):
972 # document subtitle calls SkipNode
972 # document subtitle calls SkipNode
973 self.body.append(self.defs['strong'][1]+'\n.PP\n')
973 self.body.append(self.defs['strong'][1]+'\n.PP\n')
974
974
975 def visit_system_message(self, node):
975 def visit_system_message(self, node):
976 # TODO add report_level
976 # TODO add report_level
977 #if node['level'] < self.document.reporter['writer'].report_level:
977 #if node['level'] < self.document.reporter['writer'].report_level:
978 # Level is too low to display:
978 # Level is too low to display:
979 # raise nodes.SkipNode
979 # raise nodes.SkipNode
980 attr = {}
980 attr = {}
981 backref_text = ''
981 backref_text = ''
982 if node.hasattr('id'):
982 if node.hasattr('id'):
983 attr['name'] = node['id']
983 attr['name'] = node['id']
984 if node.hasattr('line'):
984 if node.hasattr('line'):
985 line = ', line %s' % node['line']
985 line = ', line %s' % node['line']
986 else:
986 else:
987 line = ''
987 line = ''
988 self.body.append('.IP "System Message: %s/%s (%s:%s)"\n'
988 self.body.append('.IP "System Message: %s/%s (%s:%s)"\n'
989 % (node['type'], node['level'], node['source'], line))
989 % (node['type'], node['level'], node['source'], line))
990
990
991 def depart_system_message(self, node):
991 def depart_system_message(self, node):
992 pass
992 pass
993
993
994 def visit_table(self, node):
994 def visit_table(self, node):
995 self._active_table = Table()
995 self._active_table = Table()
996
996
997 def depart_table(self, node):
997 def depart_table(self, node):
998 self.ensure_eol()
998 self.ensure_eol()
999 self.body.extend(self._active_table.as_list())
999 self.body.extend(self._active_table.as_list())
1000 self._active_table = None
1000 self._active_table = None
1001
1001
1002 def visit_target(self, node):
1002 def visit_target(self, node):
1003 # targets are in-document hyper targets, without any use for man-pages.
1003 # targets are in-document hyper targets, without any use for man-pages.
1004 raise nodes.SkipNode
1004 raise nodes.SkipNode
1005
1005
1006 def visit_tbody(self, node):
1006 def visit_tbody(self, node):
1007 pass
1007 pass
1008
1008
1009 def depart_tbody(self, node):
1009 def depart_tbody(self, node):
1010 pass
1010 pass
1011
1011
1012 def visit_term(self, node):
1012 def visit_term(self, node):
1013 self.body.append(self.defs['term'][0])
1013 self.body.append(self.defs['term'][0])
1014
1014
1015 def depart_term(self, node):
1015 def depart_term(self, node):
1016 self.body.append(self.defs['term'][1])
1016 self.body.append(self.defs['term'][1])
1017
1017
1018 def visit_tgroup(self, node):
1018 def visit_tgroup(self, node):
1019 pass
1019 pass
1020
1020
1021 def depart_tgroup(self, node):
1021 def depart_tgroup(self, node):
1022 pass
1022 pass
1023
1023
1024 def visit_thead(self, node):
1024 def visit_thead(self, node):
1025 # MAYBE double line '='
1025 # MAYBE double line '='
1026 pass
1026 pass
1027
1027
1028 def depart_thead(self, node):
1028 def depart_thead(self, node):
1029 # MAYBE double line '='
1029 # MAYBE double line '='
1030 pass
1030 pass
1031
1031
1032 def visit_tip(self, node):
1032 def visit_tip(self, node):
1033 self.visit_admonition(node, 'tip')
1033 self.visit_admonition(node, 'tip')
1034
1034
1035 depart_tip = depart_admonition
1035 depart_tip = depart_admonition
1036
1036
1037 def visit_title(self, node):
1037 def visit_title(self, node):
1038 if isinstance(node.parent, nodes.topic):
1038 if isinstance(node.parent, nodes.topic):
1039 self.body.append(self.defs['topic-title'][0])
1039 self.body.append(self.defs['topic-title'][0])
1040 elif isinstance(node.parent, nodes.sidebar):
1040 elif isinstance(node.parent, nodes.sidebar):
1041 self.body.append(self.defs['sidebar-title'][0])
1041 self.body.append(self.defs['sidebar-title'][0])
1042 elif isinstance(node.parent, nodes.admonition):
1042 elif isinstance(node.parent, nodes.admonition):
1043 self.body.append('.IP "')
1043 self.body.append('.IP "')
1044 elif self.section_level == 0:
1044 elif self.section_level == 0:
1045 self._docinfo['title'] = node.astext()
1045 self._docinfo['title'] = node.astext()
1046 # document title for .TH
1046 # document title for .TH
1047 self._docinfo['title_upper'] = node.astext().upper()
1047 self._docinfo['title_upper'] = node.astext().upper()
1048 raise nodes.SkipNode
1048 raise nodes.SkipNode
1049 elif self.section_level == 1:
1049 elif self.section_level == 1:
1050 self.body.append('.SH ')
1050 self.body.append('.SH ')
1051 else:
1051 else:
1052 self.body.append('.SS ')
1052 self.body.append('.SS ')
1053
1053
1054 def depart_title(self, node):
1054 def depart_title(self, node):
1055 if isinstance(node.parent, nodes.admonition):
1055 if isinstance(node.parent, nodes.admonition):
1056 self.body.append('"')
1056 self.body.append('"')
1057 self.body.append('\n')
1057 self.body.append('\n')
1058
1058
1059 def visit_title_reference(self, node):
1059 def visit_title_reference(self, node):
1060 """inline citation reference"""
1060 """inline citation reference"""
1061 self.body.append(self.defs['title_reference'][0])
1061 self.body.append(self.defs['title_reference'][0])
1062
1062
1063 def depart_title_reference(self, node):
1063 def depart_title_reference(self, node):
1064 self.body.append(self.defs['title_reference'][1])
1064 self.body.append(self.defs['title_reference'][1])
1065
1065
1066 def visit_topic(self, node):
1066 def visit_topic(self, node):
1067 pass
1067 pass
1068
1068
1069 def depart_topic(self, node):
1069 def depart_topic(self, node):
1070 pass
1070 pass
1071
1071
1072 def visit_sidebar(self, node):
1072 def visit_sidebar(self, node):
1073 pass
1073 pass
1074
1074
1075 def depart_sidebar(self, node):
1075 def depart_sidebar(self, node):
1076 pass
1076 pass
1077
1077
1078 def visit_rubric(self, node):
1078 def visit_rubric(self, node):
1079 pass
1079 pass
1080
1080
1081 def depart_rubric(self, node):
1081 def depart_rubric(self, node):
1082 pass
1082 pass
1083
1083
1084 def visit_transition(self, node):
1084 def visit_transition(self, node):
1085 # .PP Begin a new paragraph and reset prevailing indent.
1085 # .PP Begin a new paragraph and reset prevailing indent.
1086 # .sp N leaves N lines of blank space.
1086 # .sp N leaves N lines of blank space.
1087 # .ce centers the next line
1087 # .ce centers the next line
1088 self.body.append('\n.sp\n.ce\n----\n')
1088 self.body.append('\n.sp\n.ce\n----\n')
1089
1089
1090 def depart_transition(self, node):
1090 def depart_transition(self, node):
1091 self.body.append('\n.ce 0\n.sp\n')
1091 self.body.append('\n.ce 0\n.sp\n')
1092
1092
1093 def visit_version(self, node):
1093 def visit_version(self, node):
1094 self.visit_docinfo_item(node, 'version')
1094 self.visit_docinfo_item(node, 'version')
1095
1095
1096 def visit_warning(self, node):
1096 def visit_warning(self, node):
1097 self.visit_admonition(node, 'warning')
1097 self.visit_admonition(node, 'warning')
1098
1098
1099 depart_warning = depart_admonition
1099 depart_warning = depart_admonition
1100
1100
1101 def unimplemented_visit(self, node):
1101 def unimplemented_visit(self, node):
1102 raise NotImplementedError('visiting unimplemented node type: %s'
1102 raise NotImplementedError('visiting unimplemented node type: %s'
1103 % node.__class__.__name__)
1103 % node.__class__.__name__)
1104
1104
1105 # The following part is taken from the Docutils rst2man.py script:
1105 # The following part is taken from the Docutils rst2man.py script:
1106 if __name__ == "__main__":
1106 if __name__ == "__main__":
1107 from docutils.core import publish_cmdline, default_description
1107 from docutils.core import publish_cmdline, default_description
1108 description = ("Generates plain unix manual documents. " +
1108 description = ("Generates plain unix manual documents. " +
1109 default_description)
1109 default_description)
1110 publish_cmdline(writer=Writer(), description=description)
1110 publish_cmdline(writer=Writer(), description=description)
1111
1111
1112 # vim: set fileencoding=utf-8 et ts=4 ai :
1112 # vim: set fileencoding=utf-8 et ts=4 ai :
General Comments 0
You need to be logged in to leave comments. Login now