source: doc/user-guide/genhelp.py @ fd45e859

Last change on this file since fd45e859 was fd45e859, checked in by dequis <dx@…>, at 2015-06-04T17:13:22Z

Allow building docs from any directory

Also just remove the .git check completely - just rely on make skipping
it if it exists already.

  • Property mode set to 100644
File size: 6.6 KB
Line 
1#!/usr/bin/env python
2
3# Usage: python genhelp.py input.xml output.txt
4# (Both python2 (>=2.5) or python3 work)
5
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License
8# as published by the Free Software Foundation; either version 2
9# of the License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor,
19# Boston, MA  02110-1301, USA.
20
21
22import os
23import re
24import sys
25import xml.etree.ElementTree as ET
26
27NORMALIZE_RE = re.compile(r"([^<>\s\t])[\s\t]+([^<>\s\t])")
28
29# Helpers
30
31def normalize(x):
32    """Normalize whitespace of a string.
33
34    The regexp turns any sequence of whitespace into a single space if it's in
35    the middle of the tag text, and then all newlines and tabs are removed,
36    keeping spaces.
37    """
38
39    x = NORMALIZE_RE.sub(r"\1 \2", x or '')
40    return x.replace("\n", "").replace("\t", "")
41
42def join(list):
43    """Turns any iterator into a string"""
44    return ''.join([str(x) for x in list])
45
46def fix_tree(tag, debug=False, lvl=''):
47    """Walks the XML tree and modifies it in-place fixing various details"""
48
49    # The include tags have an ugly namespace in the tag name. Simplify that.
50    if tag.tag.count("XInclude"):
51        tag.tag = 'include'
52
53    # Print a pretty tree-like representation of the processed tags
54    if debug:
55        print("%s<%s>%r" % (lvl, tag.tag, [tag.text, normalize(tag.text)]))
56
57    for subtag in tag:
58        fix_tree(subtag, debug, lvl + "  ")
59
60    if debug:
61        print("%s</%s>%r" % (lvl, tag.tag, [tag.tail, normalize(tag.tail)]))
62
63    # Actually normalize whitespace
64    tag.text = normalize(tag.text)
65    tag.tail = normalize(tag.tail)
66
67
68# Main logic
69
70def process_file(filename, parent=None):
71    tree = ET.parse(open(filename)).getroot()
72    fix_tree(tree)
73    return parse_tag(tree, parent)
74
75def parse_tag(tag, parent):
76    """Calls a tag_... function based on the tag name"""
77
78    fun = globals()["tag_%s" % tag.tag.replace("-", "_")]
79    return join(fun(tag, parent))
80
81def parse_subtags(tag, parent=None):
82    yield tag.text
83
84    for subtag in tag:
85        yield parse_tag(subtag, tag)
86
87    yield tag.tail
88
89
90# Main tag handlers
91
92def handle_subject(tag, parent):
93    """Tag handler for preface, chapter, sect1 and sect2 (aliased below)"""
94
95    yield '?%s\n' % tag.attrib['id']
96
97    first = True
98    for element in tag:
99        if element.tag in ["para", "variablelist", "simplelist",
100                           "command-list", "ircexample"]:
101            if not first:
102                # Spaces between paragraphs
103                yield "\n"
104            first = False
105
106            if element.attrib.get('title', ''):
107                yield element.attrib['title']
108                yield "\n"
109            yield join(parse_tag(element, tag)).rstrip("\n")
110            yield "\n"
111
112    yield "%\n"
113
114    for element in tag:
115        if element.tag in ["sect1", "sect2"]:
116            yield join(handle_subject(element, tag))
117
118    for element in tag.findall("bitlbee-command"):
119        yield join(handle_command(element))
120
121    for element in tag.findall("bitlbee-setting"):
122        yield join(handle_setting(element))
123
124def handle_command(tag, prefix=''):
125    """Tag handler for <bitlbee-command> (called from handle_subject)"""
126
127    this_cmd = prefix + tag.attrib['name']
128
129    yield "?%s\n" % this_cmd
130    for syntax in tag.findall("syntax"):
131        yield '\x02Syntax:\x02 %s\n' % syntax.text
132
133    yield "\n"
134    yield join(parse_subtags(tag.find("description"))).rstrip()
135    yield "\n"
136
137    for example in tag.findall("ircexample"):
138        yield "\n\x02Example:\x02\n"
139        yield join(parse_subtags(example)).rstrip()
140        yield "\n"
141
142    yield "%\n"
143
144    for element in tag.findall("bitlbee-command"):
145        yield join(handle_command(element, this_cmd + " "))
146
147def handle_setting(tag):
148    """Tag handler for <bitlbee-setting> (called from handle_subject)"""
149
150    yield "?set %s\n" % tag.attrib['name']
151    yield "\x02Type:\x02 %s\n" % tag.attrib["type"]
152    yield "\x02Scope:\x02 %s\n" % tag.attrib["scope"]
153
154    if tag.find("default") is not None:
155        yield "\x02Default:\x02 %s\n" % tag.findtext("default")
156
157    if tag.find("possible-values") is not None:
158        yield "\x02Possible Values:\x02 %s\n" % tag.findtext("possible-values")
159
160    yield "\n"
161    yield join(parse_subtags(tag.find("description"))).rstrip()
162    yield "\n%\n"
163
164
165# Aliases for tags that behave like subjects
166tag_preface = handle_subject
167tag_chapter = handle_subject
168tag_sect1 = handle_subject
169tag_sect2 = handle_subject
170
171# Aliases for tags that don't have any special behavior
172tag_ulink = parse_subtags
173tag_note = parse_subtags
174tag_book = parse_subtags
175tag_ircexample = parse_subtags
176
177
178# Handlers for specific tags
179
180def tag_include(tag, parent):
181    return process_file(tag.attrib['href'], tag)
182
183def tag_para(tag, parent):
184    return join(parse_subtags(tag)) + "\n\n"
185
186def tag_emphasis(tag, parent):
187    return "\x02%s\x02%s" % (tag.text, tag.tail)
188
189def tag_ircline(tag, parent):
190    return "\x02<%s>\x02 %s\n" % (tag.attrib['nick'], join(parse_subtags(tag)))
191
192def tag_ircaction(tag, parent):
193    return "\x02* %s\x02 %s\n" % (tag.attrib['nick'], join(parse_subtags(tag)))
194
195def tag_command_list(tag, parent):
196    yield "These are all root commands. See \x02help <command name>\x02 " \
197          "for more details on each command.\n\n"
198
199    for subtag in parent.findall("bitlbee-command"):
200        yield " * \x02%s\x02 - %s\n" % \
201            (subtag.attrib['name'],
202             subtag.findtext("short-description"))
203
204    yield "\nMost commands can be shortened. For example instead of " \
205          "\x02account list\x02, try \x02ac l\x02.\n\n"
206
207def tag_variablelist(tag, parent):
208    for subtag in tag:
209        yield " \x02%s\x02 - %s\n" % \
210            (subtag.findtext("term"),
211             join(parse_subtags(subtag.find("listitem/para"))))
212    yield '\n'
213
214def tag_simplelist(tag, parent):
215    for subtag in tag:
216        yield " - %s\n" % join(parse_subtags(subtag))
217    yield '\n'
218
219
220def main():
221    if len(sys.argv) != 3:
222        print("Usage: python genhelp.py input.xml output.txt")
223        return
224
225    # ensure that we really are in the same directory as the input file
226    os.chdir(os.path.dirname(os.path.abspath(sys.argv[1])))
227
228    txt = process_file(sys.argv[1])
229    open(sys.argv[2], "w").write(txt)
230
231if __name__ == '__main__':
232    main()
Note: See TracBrowser for help on using the repository browser.