2929"""
3030
3131import os
32+ import re
3233import sys
3334
3435import codecs
5657TD = '{http://projectmallard.org/1.0/}td'
5758OUTPUT = '{http://projectmallard.org/1.0/}output'
5859
60+ # Matches "\" and "-", but not "\-".
61+ replaceables = re .compile (r'(\\(?!-))|((?<!\\)-)' )
62+
63+
5964class Convert (object ):
6065 title = None
6166 subtitle = None
@@ -69,6 +74,9 @@ def __init__(self, inFile, outFile, section):
6974 self .section = section
7075 self .sections = []
7176
77+ # Map: section id -> section element.
78+ self .sections_map = {}
79+
7280 def _parse (self ):
7381 self .tree = ElementTree .ElementTree ()
7482 self .tree .parse (open (self .inFile ))
@@ -87,23 +95,57 @@ def _get_parent(self, ele):
8795 return self .parent_map [ele ]
8896
8997 def _extract (self ):
90- # Try to extract the title.
98+ # Extract the title and subtitle .
9199 for child in self .root .getchildren ():
92100 if child .tag == TITLE :
93101 self .title = child .text .strip ()
94102 elif child .tag == SUBTITLE :
95103 self .subtitle = child .text .strip ()
96104 elif child .tag == SECTION :
105+ if child .get ('id' ):
106+ self .sections_map [child .get ('id' )] = child
97107 self .sections .append (child )
98108
109+ if not self .subtitle and 'description' in self .sections_map :
110+ # No "subtitle" element, use description section title as subtitle.
111+ self .subtitle = self ._section_text (self .sections_map ['description' ])
112+
113+ def _section_text (self , section ):
114+ # Find <section id="description"><p>some text</p></section>.
115+ for child in section :
116+ if child .tag != TITLE :
117+ return self ._textify_elem (child )
118+
119+ def _textify_elem (self , elem ):
120+ return '' .join (elem .itertext ()).strip ()
121+
99122 def _writeComment (self , text = '' ):
100123 lines = text .split ('\n ' )
101124 for line in lines :
102125 self .outFile .write ('.\\ " ' )
103126 self .outFile .write (line )
104127 self .outFile .write ('\n ' )
105128
129+ def _escape_char (self , match ):
130+ c = match .group (0 )
131+ if c == "-" :
132+ return r"\(hy"
133+ elif c == "\\ " :
134+ return "\\ e"
135+
136+ assert False , "invalid char passed to _escape_char: %r" % c
137+
138+ def _escape (self , text ):
139+ # Avoid "hyphen-used-as-minus-sign" lintian warning about man pages,
140+ # and escape text like "\0" as "\\0". We'll replace all "-" with "\(hy",
141+ # which is an explicit hyphen, but leave alone the first line's
142+ # "name \- description" text.
143+ return replaceables .sub (self ._escape_char , text )
144+
106145 def _write (self , text ):
146+ self ._write_noescape (self ._escape (text ))
147+
148+ def _write_noescape (self , text ):
107149 self .outFile .write (text )
108150
109151 def _writeCommand (self , text ):
@@ -135,11 +177,7 @@ def _generateHeader(self):
135177 title = self .title .replace ('()' ,'' ).upper ()
136178 self ._write ('.TH "%s" "%s" "%s" "%s"\n ' % (title , self .section , date , GROUP ))
137179 self ._write ('.SH NAME\n ' )
138-
139- if self .subtitle :
140- self ._write ('%s \\ - %s\n ' % (self .title , self .subtitle ))
141- else :
142- self ._write ('%s\n ' % self .title )
180+ self ._write_noescape ('%s \\ - %s\n ' % (self .title , self .subtitle ))
143181
144182 def _generateSection (self , section ):
145183 # Try to render the title first
@@ -165,7 +203,7 @@ def _generateCode(self, code):
165203 is_synopsis = self ._get_parent (code ).tag .endswith ('synopsis' )
166204 if text and '\n ' not in text and not is_synopsis :
167205 text = text .replace ('()' , '(%s)' % self .section )
168- self ._writeCommand ('.BR ' + text )
206+ self ._writeCommand ('.B ' + text )
169207 else :
170208 self ._writeCommand ('.nf' )
171209 self ._writeLine (code .text )
@@ -203,7 +241,7 @@ def _generateList(self, l):
203241 self ._generateElement (child )
204242
205243 def _generateEM (self , em ):
206- self ._writeCommand ('.BR %s' % em .text )
244+ self ._writeCommand ('.B %s' % em .text )
207245
208246 def _generateOutput (self , output ):
209247 self ._generateCode (output )
@@ -274,18 +312,17 @@ def _generateLink(self, link):
274312 if text and '()' in text :
275313 text = text .replace ('()' , '(%s)' % self .section )
276314 if text :
277- self ._writeCommand ('.BR ' + text )
315+ self ._writeCommand ('.B ' + text )
278316
279317 def _generateSections (self ):
280318 for section in self .sections :
281319 self ._generateElement (section )
282320
283321 def _generateFooter (self ):
284- self ._write ('\n .BR ' )
322+ self ._write ('\n .B ' )
285323 self ._write ('\n .SH COLOPHON' )
286324 self ._write ('\n This page is part of %s.' % GROUP )
287- self ._write ('\n Please report any bugs at\n ' )
288- self ._write ('\\ %' + BUG_URL .replace ('-' ,'\\ -' ) + '.' )
325+ self ._write ('\n Please report any bugs at %s.' % BUG_URL .replace ('-' ,'\\ -' ))
289326
290327 def _generate (self ):
291328 self .realname = self .outFile
0 commit comments