~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzr-man.py

  • Committer: Martin Pool
  • Date: 2005-09-12 09:50:44 UTC
  • Revision ID: mbp@sourcefrog.net-20050912095044-6acfdb5611729987
- no tests in bzrlib.fetch anymore

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2005 Canonical Ltd.
 
1
#!/usr/bin/python
 
2
 
 
3
# Copyright (C) 2005 by Hans Ulrich Niedermann
 
4
# Portions Copyright (C) 2005 by Canonical Ltd
2
5
 
3
6
# This program is free software; you can redistribute it and/or modify
4
7
# it under the terms of the GNU General Public License as published by
14
17
# along with this program; if not, write to the Free Software
15
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
19
 
17
 
"""man.py - create man page from built-in bzr help and static text
18
 
 
19
 
TODO:
20
 
  * use usage information instead of simple "bzr foo" in COMMAND OVERVIEW
21
 
  * add command aliases
22
 
"""
23
 
 
24
 
import os
25
 
import sys
26
 
import textwrap
 
20
#<<< code taken from bzr (C) Canonical
 
21
 
 
22
import os, sys
 
23
 
 
24
try:
 
25
    version_info = sys.version_info
 
26
except AttributeError:
 
27
    version_info = 1, 5 # 1.5 or older
 
28
 
 
29
 
 
30
REINVOKE = "__BZR_REINVOKE"    
 
31
NEED_VERS = (2, 3)
 
32
 
 
33
if version_info < NEED_VERS:
 
34
    if not os.environ.has_key(REINVOKE):
 
35
        # mutating os.environ doesn't work in old Pythons
 
36
        os.putenv(REINVOKE, "1")
 
37
        for python in 'python2.4', 'python2.3':
 
38
            try:
 
39
                os.execvp(python, [python] + sys.argv)
 
40
            except OSError:
 
41
                pass
 
42
    print >>sys.stderr, "bzr-man.py: error: cannot find a suitable python interpreter"
 
43
    print >>sys.stderr, "  (need %d.%d or later)" % NEED_VERS
 
44
    sys.exit(1)
 
45
os.unsetenv(REINVOKE)
 
46
 
 
47
import bzrlib, bzrlib.help
 
48
 
 
49
#>>> code taken from bzr (C) Canonical
 
50
 
 
51
#<<< code by HUN
 
52
 
27
53
import time
28
 
 
29
 
import bzrlib
30
 
import bzrlib.help
31
 
import bzrlib.commands
32
 
 
33
 
 
34
 
def get_filename(options):
35
 
    """Provides name of manpage"""
36
 
    return "%s.1" % (options.bzr_name)
37
 
 
38
 
 
39
 
def infogen(options, outfile):
40
 
    """Assembles a man page"""
41
 
    t = time.time()
42
 
    tt = time.gmtime(t)
43
 
    params = \
44
 
           { "bzrcmd": options.bzr_name,
45
 
             "datestamp": time.strftime("%Y-%m-%d",tt),
46
 
             "timestamp": time.strftime("%Y-%m-%d %H:%M:%S +0000",tt),
47
 
             "version": bzrlib.__version__,
48
 
             }
49
 
    outfile.write(man_preamble % params)
50
 
    outfile.write(man_escape(man_head % params))
51
 
    outfile.write(man_escape(getcommand_list(params)))
52
 
    outfile.write(man_escape(getcommand_help(params)))
53
 
    outfile.write(man_escape(man_foot % params))
 
54
import re
54
55
 
55
56
 
56
57
def man_escape(string):
57
 
    """Escapes strings for man page compatibility"""
58
58
    result = string.replace("\\","\\\\")
59
59
    result = result.replace("`","\\`")
60
60
    result = result.replace("'","\\'")
62
62
    return result
63
63
 
64
64
 
65
 
def command_name_list():
66
 
    """Builds a list of command names from bzrlib"""
67
 
    command_names = bzrlib.commands.builtin_command_names()
68
 
    command_names.sort()
69
 
    return command_names
70
 
 
71
 
 
72
 
def getcommand_list (params):
73
 
    """Builds summary help for command names in manpage format"""
74
 
    bzrcmd = params["bzrcmd"]
75
 
    output = '.SH "COMMAND OVERVIEW"\n'
76
 
    for cmd_name in command_name_list():
77
 
        cmd_object = bzrlib.commands.get_cmd_object(cmd_name)
78
 
        if cmd_object.hidden:
79
 
            continue
80
 
        cmd_help = cmd_object.help()
81
 
        if cmd_help:
82
 
            firstline = cmd_help.split('\n', 1)[0]
83
 
            usage = bzrlib.help.command_usage(cmd_object)
84
 
            tmp = '.TP\n.B "%s"\n%s\n' % (usage, firstline)
85
 
            output = output + tmp
 
65
class Parser:
 
66
 
 
67
    def parse_line(self, line):
 
68
        pass
 
69
 
 
70
 
 
71
class CommandListParser(Parser):
 
72
 
 
73
    """Parser for output of "bzr help commands".
 
74
 
 
75
    The parsed content can then be used to
 
76
    - write a "COMMAND OVERVIEW" section into a man page
 
77
    - provide a list of all commands
 
78
    """
 
79
 
 
80
    def __init__(self,params):
 
81
        self.params = params
 
82
        self.command_usage = []
 
83
        self.all_commands = []
 
84
        self.usage_exp = re.compile("([a-z0-9-]+).*")
 
85
        self.descr_exp = re.compile("    ([A-Z].*)\s*")
 
86
        self.state = 0
 
87
        self.command = None
 
88
        self.usage = None
 
89
        self.descr = None
 
90
 
 
91
    def parse_line(self, line):
 
92
        m = self.usage_exp.match(line)
 
93
        if m:
 
94
            if self.state == 0:
 
95
                if self.usage:
 
96
                    self.command_usage.append((self.command,self.usage,self.descr))
 
97
                    self.all_commands.append(self.command)
 
98
                self.usage = line
 
99
                self.command = m.groups()[0]
 
100
            else:
 
101
                raise Error, "matching usage line in state %d" % state
 
102
            self.state = 1
 
103
            return
 
104
        m = self.descr_exp.match(line)
 
105
        if m:
 
106
            if self.state == 1:
 
107
                self.descr = m.groups()[0]
 
108
            else:
 
109
                raise Error, "matching descr line in state %d" % state
 
110
            self.state = 0
 
111
            return
 
112
        raise Error, "Cannot parse this line"
 
113
 
 
114
    def end_parse(self):
 
115
        if self.state == 0:
 
116
            if self.usage:
 
117
                self.command_usage.append((self.command,self.usage,self.descr))
 
118
                self.all_commands.append(self.command)
86
119
        else:
87
 
            raise RuntimeError, "Command '%s' has no help text" % (cmd_name)
88
 
    return output
89
 
 
90
 
 
91
 
def getcommand_help(params):
92
 
    """Shows individual options for a bzr command"""
93
 
    output='.SH "COMMAND REFERENCE"\n'
94
 
    for cmd_name in command_name_list():
95
 
        cmd_object = bzrlib.commands.get_cmd_object(cmd_name)
96
 
        if cmd_object.hidden:
97
 
            continue
98
 
        output = output + format_command(params, cmd_object)
99
 
    return output
100
 
 
101
 
 
102
 
def format_command (params, cmd):
103
 
    """Provides long help for each public command"""
104
 
    subsection_header = '.SS "%s"\n' % (bzrlib.help.command_usage(cmd))
105
 
    doc = "%s\n" % (cmd.__doc__)
106
 
    docsplit = cmd.__doc__.split('\n')
107
 
    doc = '\n'.join([docsplit[0]] + [line[4:] for line in docsplit[1:]])
108
 
    option_str = ""
109
 
    options = cmd.options()
110
 
    if options:
111
 
        option_str = "\nOptions:\n"
112
 
        for option_name, option in sorted(options.items()):
113
 
            l = '    --' + option_name
114
 
            if option.type is not None:
115
 
                l += ' ' + option.argname.upper()
116
 
            short_name = option.short_name()
117
 
            if short_name:
118
 
                assert len(short_name) == 1
119
 
                l += ', -' + short_name
120
 
            l += (30 - len(l)) * ' ' + option.help
121
 
            # TODO: Split help over multiple lines with
122
 
            # correct indenting and wrapping.
123
 
            wrapped = textwrap.fill(l, initial_indent='',
124
 
                                    subsequent_indent=30*' ')
125
 
            option_str = option_str + wrapped + '\n'       
126
 
    return subsection_header + option_str + "\n" + doc + "\n"
 
120
            raise Error, "ending parse in state %d" % state
 
121
 
 
122
    def write_to_manpage(self, outfile):
 
123
        bzrcmd = self.params["bzrcmd"]
 
124
        outfile.write('.SH "COMMAND OVERVIEW"\n')
 
125
        for (command,usage,descr) in self.command_usage:
 
126
            outfile.write('.TP\n.B "%s %s"\n%s\n\n' % (bzrcmd, usage, descr))
 
127
 
 
128
 
 
129
class HelpReader:
 
130
 
 
131
    def __init__(self, parser):
 
132
        self.parser = parser
 
133
 
 
134
    def write(self, data):
 
135
        if data[-1] == '\n':
 
136
            data = data[:-1]
 
137
        for line in data.split('\n'):
 
138
            self.parser.parse_line(line)
 
139
 
 
140
 
 
141
def write_command_details(params, command, usage, descr, outfile):
 
142
    x = ('.SS "%s %s"\n.B "%s"\n.PP\n.B "Usage:"\n%s %s\n\n' %
 
143
         (params["bzrcmd"],
 
144
          command,
 
145
          descr,
 
146
          params["bzrcmd"],
 
147
          usage))
 
148
    outfile.write(man_escape(x))
127
149
 
128
150
 
129
151
man_preamble = """\
130
 
Man page for %(bzrcmd)s (bazaar-ng)
 
152
.\\\" Man page for %(bzrcmd)s (bazaar-ng)
131
153
.\\\"
132
154
.\\\" Large parts of this file are autogenerated from the output of
133
155
.\\\"     \"%(bzrcmd)s help commands\"
137
159
.\\\"
138
160
"""
139
161
 
140
 
 
 
162
# The DESCRIPTION text was taken from http://www.bazaar-ng.org/
 
163
# and is thus (C) Canonical
141
164
man_head = """\
142
165
.TH bzr 1 "%(datestamp)s" "%(version)s" "bazaar-ng"
143
166
.SH "NAME"
170
193
Path where
171
194
.B "%(bzrcmd)s"
172
195
is to look for external command.
 
196
 
173
197
.TP
174
198
.I "BZREMAIL"
175
 
E-Mail address of the user. Overrides default user config.
 
199
E-Mail address of the user. Overrides
 
200
.I "~/.bzr.conf/email" and
 
201
.IR "EMAIL" .
 
202
Example content:
 
203
.I "John Doe <john@example.com>"
 
204
 
176
205
.TP
177
206
.I "EMAIL"
178
 
E-Mail address of the user. Overriddes default user config.
 
207
E-Mail address of the user. Overridden by the content of the file
 
208
.I "~/.bzr.conf/email"
 
209
and of the environment variable
 
210
.IR "BZREMAIL" .
 
211
 
179
212
.SH "FILES"
180
213
.TP
181
 
.I "~/.bazaar/bazaar.conf/"
182
 
Contains the default user config. Only one section, [DEFAULT] is allowed. A 
183
 
typical default config file may be similiar to:
184
 
.br
185
 
.br
186
 
.B [DEFAULT]
187
 
.br
188
 
.B email=John Doe <jdoe@isp.com>
 
214
.I "~/.bzr.conf/"
 
215
Directory where all the user\'s settings are stored.
 
216
.TP
 
217
.I "~/.bzr.conf/email"
 
218
Stores name and email address of the user. Overrides content of
 
219
.I "EMAIL"
 
220
environment variable. Example content:
 
221
.I "John Doe <john@example.com>"
 
222
 
189
223
.SH "SEE ALSO"
190
 
.UR http://bazaar.canonical.com/
191
 
.BR http://bazaar.canonical.com/,
192
224
.UR http://www.bazaar-ng.org/
193
 
.BR http://www.bazaar-ng.org/
 
225
.BR http://www.bazaar-ng.org/,
 
226
.UR http://www.bazaar-ng.org/doc/
 
227
.BR http://www.bazaar-ng.org/doc/
194
228
"""
195
229
 
 
230
def main():
 
231
    t = time.time()
 
232
    tt = time.gmtime(t)
 
233
    params = \
 
234
           { "bzrcmd": "bzr",
 
235
             "datestamp": time.strftime("%Y-%m-%d",tt),
 
236
             "timestamp": time.strftime("%Y-%m-%d %H:%M:%S +0000",tt),
 
237
             "version": bzrlib.__version__,
 
238
             }
 
239
 
 
240
    clp = CommandListParser(params)
 
241
    bzrlib.help.help("commands", outfile=HelpReader(clp))
 
242
    clp.end_parse()
 
243
 
 
244
    filename = "bzr.1"
 
245
    if len(sys.argv) == 2:
 
246
        filename = sys.argv[1]
 
247
    if filename == "-":
 
248
        outfile = sys.stdout
 
249
    else:
 
250
        outfile = open(filename,"w")
 
251
 
 
252
    outfile.write(man_preamble % params)
 
253
    outfile.write(man_escape(man_head % params))
 
254
    clp.write_to_manpage(outfile)
 
255
 
 
256
    # FIXME:
 
257
    #   This doesn't do more than the summary so far.
 
258
    #outfile.write('.SH "DETAILED COMMAND DESCRIPTION"\n')
 
259
    #for (command,usage,descr) in clp.command_usage:
 
260
    #    write_command_details(params, command, usage, descr, outfile = outfile)
 
261
 
 
262
    outfile.write(man_escape(man_foot % params))
 
263
 
 
264
 
 
265
if __name__ == '__main__':
 
266
    main()
 
267
 
 
268
 
 
269
#>>> code by HUN