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