source: doc/user-guide/genhelp.py @ 63cad66

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

configure: allow specifying location of python executable

Because openbsd/netbsd don't have a 'python' in the PATH unless the user
creates the symlink explicitly.

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