summaryrefslogtreecommitdiff
path: root/bin/scons-proc.py
diff options
context:
space:
mode:
Diffstat (limited to 'bin/scons-proc.py')
-rw-r--r--bin/scons-proc.py174
1 files changed, 131 insertions, 43 deletions
diff --git a/bin/scons-proc.py b/bin/scons-proc.py
index 7cdb618..6d15816 100644
--- a/bin/scons-proc.py
+++ b/bin/scons-proc.py
@@ -10,10 +10,11 @@
# and/or .mod files contining the ENTITY definitions for each item,
# or in man-page-formatted output.
#
-import os
-import sys
import getopt
+import os
import re
+import string
+import sys
import xml.sax
try:
from io import StringIO
@@ -153,9 +154,7 @@ class SCons_XML_to_XML(SCons_XML):
for v in self.values:
f.write('\n<varlistentry id="%s%s">\n' %
(v.prefix, v.idfunc()))
- for term in v.termfunc():
- f.write('<term><%s>%s</%s></term>\n' %
- (v.tag, term, v.tag))
+ f.write('%s\n' % v.xml_term())
f.write('<listitem>\n')
for chunk in v.summary.body:
f.write(str(chunk))
@@ -212,23 +211,41 @@ class SCons_XML_to_XML(SCons_XML):
class SCons_XML_to_man(SCons_XML):
def write(self, filename):
+ """
+ Converts the contents of the specified filename from DocBook XML
+ to man page macros.
+
+ This does not do an intelligent job. In particular, it doesn't
+ actually use the structured nature of XML to handle arbitrary
+ input. Instead, we're using text replacement and regular
+ expression substitutions to convert observed patterns into the
+ macros we want. To the extent that we're relatively consistent
+ with our input .xml, this works, but could easily break if handed
+ input that doesn't match these specific expectations.
+ """
if not filename:
return
f = self.fopen(filename)
chunks = []
for v in self.values:
- chunks.extend(v.mansep())
- chunks.extend(v.initial_chunks())
+ chunks.extend(v.man_separator())
+ chunks.extend(v.initial_man_chunks())
chunks.extend(list(map(str, v.summary.body)))
body = ''.join(chunks)
+
+ # Simple transformation of examples into our defined macros for those.
body = body.replace('<programlisting>', '.ES')
body = body.replace('</programlisting>', '.EE')
+
+ # Replace groupings of <para> tags and surrounding newlines
+ # with single blank lines.
body = body.replace('\n</para>\n<para>\n', '\n\n')
body = body.replace('<para>\n', '')
body = body.replace('<para>', '\n')
body = body.replace('</para>\n', '')
+ # Convert <variablelist> and its child tags.
body = body.replace('<variablelist>\n', '.RS 10\n')
# Handling <varlistentry> needs to be rationalized and made
# consistent. Right now, the <term> values map to arbitrary,
@@ -240,35 +257,54 @@ class SCons_XML_to_man(SCons_XML):
body = body.replace('</varlistentry>\n', '')
body = body.replace('</variablelist>\n', '.RE\n')
+ # Get rid of unnecessary .IP macros, and unnecessary blank lines
+ # in front of .IP macros.
body = re.sub(r'\.EE\n\n+(?!\.IP)', '.EE\n.IP\n', body)
+ body = body.replace('\n.EE\n.IP\n.ES\n', '\n.EE\n\n.ES\n')
body = body.replace('\n.IP\n\'\\"', '\n\n\'\\"')
- body = re.sub('&(scons|SConstruct|SConscript|jar|Make|lambda);', r'\\fB\1\\fP', body)
+
+ # Convert various named entities and tagged names to nroff
+ # in-line font conversions (\fB, \fI, \fP).
+ body = re.sub('&(scons|SConstruct|SConscript|Dir|jar|Make|lambda);',
+ r'\\fB\1\\fP', body)
body = re.sub('&(TARGET|TARGETS|SOURCE|SOURCES);', r'\\fB$\1\\fP', body)
- body = body.replace('&Dir;', r'\fBDir\fP')
- body = body.replace('&target;', r'\fItarget\fP')
- body = body.replace('&source;', r'\fIsource\fP')
+ body = re.sub('&(target|source);', r'\\fI\1\\fP', body)
body = re.sub('&b(-link)?-([^;]*);', r'\\fB\2\\fP()', body)
- body = re.sub('&cv(-link)?-([^;]*);', r'$\2', body)
+ body = re.sub('&cv(-link)?-([^;]*);', r'\\fB$\2\\fP', body)
body = re.sub('&f(-link)?-env-([^;]*);', r'\\fBenv.\2\\fP()', body)
body = re.sub('&f(-link)?-([^;]*);', r'\\fB\2\\fP()', body)
body = re.sub(r'<(application|command|envar|filename|function|literal|option)>([^<]*)</\1>',
r'\\fB\2\\fP', body)
body = re.sub(r'<(classname|emphasis|varname)>([^<]*)</\1>',
r'\\fI\2\\fP', body)
+
+ # Convert groupings of font conversions (\fB, \fI, \fP) to
+ # man page .B, .BR, .I, .IR, .R, .RB and .RI macros.
body = re.compile(r'^\\f([BI])([^\\]* [^\\]*)\\fP\s*$', re.M).sub(r'.\1 "\2"', body)
body = re.compile(r'^\\f([BI])(.*)\\fP\s*$', re.M).sub(r'.\1 \2', body)
body = re.compile(r'^\\f([BI])(.*)\\fP(\S+)$', re.M).sub(r'.\1R \2 \3', body)
+ body = re.compile(r'^(\.B)( .*)\\fP(.*)\\fB(.*)$', re.M).sub(r'\1R\2 \3 \4', body)
+ body = re.compile(r'^(\.B)R?( .*)\\fP(.*)\\fI(.*)$', re.M).sub(r'\1I\2\3 \4', body)
+ body = re.compile(r'^(\.I)( .*)\\fP\\fB(.*)\\fP\\fI(.*)$', re.M).sub(r'\1R\2 \3 \4', body)
body = re.compile(r'^(\S+)\\f([BI])(.*)\\fP$', re.M).sub(r'.R\2 \1 \3', body)
body = re.compile(r'^(\S+)\\f([BI])(.*)\\fP([^\s\\]+)$', re.M).sub(r'.R\2 \1 \3 \4', body)
body = re.compile(r'^(\.R[BI].*[\S])\s+$;', re.M).sub(r'\1', body)
+
+ # Convert &lt; and &gt; entities to literal < and > characters.
body = body.replace('&lt;', '<')
body = body.replace('&gt;', '>')
- body = re.sub(r'\\([^f])', r'\\\\\1', body)
+
+ # Backslashes. Oh joy.
+ body = re.sub(r'\\(?=[^f])', r'\\\\', body)
body = re.compile("^'\\\\\\\\", re.M).sub("'\\\\", body)
+ body = re.compile(r'^\.([BI]R?) ([^"]\S*\\\\\S+[^"])$', re.M).sub(r'.\1 "\2"', body)
+
+ # Put backslashes in front of various hyphens that need
+ # to be long em-dashes.
body = re.compile(r'^\.([BI]R?) --', re.M).sub(r'.\1 \-\-', body)
body = re.compile(r'^\.([BI]R?) -', re.M).sub(r'.\1 \-', body)
- body = re.compile(r'^\.([BI]R?) (\S+)\\\\(\S+)$', re.M).sub(r'.\1 "\2\\\\\\\\\2"', body)
body = re.compile(r'\\f([BI])-', re.M).sub(r'\\f\1\-', body)
+
f.write(body)
class Proxy(object):
@@ -290,34 +326,91 @@ class Proxy(object):
return cmp(self.__subject, other)
return cmp(self.__dict__, other.__dict__)
-class Builder(Proxy):
+class SConsThing(Proxy):
+ def idfunc(self):
+ return self.name
+ def xml_term(self):
+ return '<term>%s</term>' % self.name
+
+class Builder(SConsThing):
description = 'builder'
prefix = 'b-'
tag = 'function'
- def idfunc(self):
- return self.name
- def termfunc(self):
- return ['%s()' % self.name, 'env.%s()' % self.name]
+ def xml_term(self):
+ return ('<term><synopsis><%s>%s()</%s></synopsis>\n<synopsis><%s>env.%s()</%s></synopsis></term>' %
+ (self.tag, self.name, self.tag, self.tag, self.name, self.tag))
def entityfunc(self):
return self.name
- def mansep(self):
+ def man_separator(self):
return ['\n', "'\\" + '"'*69 + '\n']
- def initial_chunks(self):
- return [ '.IP %s\n' % t for t in self.termfunc() ]
+ def initial_man_chunks(self):
+ return [ '.IP %s()\n.IP env.%s()\n' % (self.name, self.name) ]
-class Function(Proxy):
+class Function(SConsThing):
description = 'function'
prefix = 'f-'
tag = 'function'
- def idfunc(self):
- return self.name
- def termfunc(self):
- return ['%s()' % self.name, 'env.%s()' % self.name]
+ def args_to_xml(self, arg):
+ s = ''.join(arg.body).strip()
+ result = []
+ for m in re.findall('([a-zA-Z/_]+=?|[^a-zA-Z/_]+)', s):
+ if m[0] in string.letters:
+ if m[-1] == '=':
+ result.append('<literal>%s</literal>=' % m[:-1])
+ else:
+ result.append('<varname>%s</varname>' % m)
+ else:
+ result.append(m)
+ return ''.join(result)
+ def xml_term(self):
+ try:
+ arguments = self.arguments
+ except AttributeError:
+ arguments = ['()']
+ result = ['<term>']
+ for arg in arguments:
+ try:
+ signature = arg.signature
+ except AttributeError:
+ signature = "both"
+ s = self.args_to_xml(arg)
+ if signature in ('both', 'global'):
+ result.append('<synopsis>%s%s</synopsis>\n' % (self.name, s)) #<br>
+ if signature in ('both', 'env'):
+ result.append('<synopsis><varname>env</varname>.%s%s</synopsis>' % (self.name, s))
+ result.append('</term>')
+ return ''.join(result)
def entityfunc(self):
return self.name
- def mansep(self):
+ def man_separator(self):
return ['\n', "'\\" + '"'*69 + '\n']
- def initial_chunks(self):
+ def args_to_man(self, arg):
+ """Converts the contents of an <arguments> tag, which
+ specifies a function's calling signature, into a series
+ of tokens that alternate between literal tokens
+ (to be displayed in roman or bold face) and variable
+ names (to be displayed in italics).
+
+ This is complicated by the presence of Python "keyword=var"
+ arguments, where "keyword=" should be displayed literally,
+ and "var" should be displayed in italics. We do this by
+ detecting the keyword= var portion and appending it to the
+ previous string, if any.
+ """
+ s = ''.join(arg.body).strip()
+ result = []
+ for m in re.findall('([a-zA-Z/_]+=?|[^a-zA-Z/_]+)', s):
+ if m[-1] == '=' and result:
+ if result[-1][-1] == '"':
+ result[-1] = result[-1][:-1] + m + '"'
+ else:
+ result[-1] += m
+ else:
+ if ' ' in m:
+ m = '"%s"' % m
+ result.append(m)
+ return ' '.join(result)
+ def initial_man_chunks(self):
try:
arguments = self.arguments
except AttributeError:
@@ -328,40 +421,35 @@ class Function(Proxy):
signature = arg.signature
except AttributeError:
signature = "both"
+ s = self.args_to_man(arg)
if signature in ('both', 'global'):
- result.append('.TP\n.RI %s%s\n' % (self.name, arg))
+ result.append('.TP\n.RI %s%s\n' % (self.name, s))
if signature in ('both', 'env'):
- result.append('.TP\n.IR env .%s%s\n' % (self.name, arg))
+ result.append('.TP\n.IR env .%s%s\n' % (self.name, s))
return result
-class Tool(Proxy):
+class Tool(SConsThing):
description = 'tool'
prefix = 't-'
tag = 'literal'
def idfunc(self):
return self.name.replace('+', 'X')
- def termfunc(self):
- return [self.name]
def entityfunc(self):
return self.name
- def mansep(self):
+ def man_separator(self):
return ['\n']
- def initial_chunks(self):
+ def initial_man_chunks(self):
return ['.IP %s\n' % self.name]
-class Variable(Proxy):
+class Variable(SConsThing):
description = 'construction variable'
prefix = 'cv-'
tag = 'envar'
- def idfunc(self):
- return self.name
- def termfunc(self):
- return [self.name]
def entityfunc(self):
return '$' + self.name
- def mansep(self):
+ def man_separator(self):
return ['\n']
- def initial_chunks(self):
+ def initial_man_chunks(self):
return ['.IP %s\n' % self.name]
if output_type == '--man':