tizen 2.4 release
[external/nghttp2.git] / help2rst.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # script to produce rst file from program's help output.
5
6 from __future__ import unicode_literals
7 import sys
8 import re
9 import argparse
10
11 arg_indent = ' ' * 14
12
13 def help2man(infile):
14     # We assume that first line is usage line like this:
15     #
16     # Usage: nghttp [OPTIONS]... URI...
17     #
18     # The second line is description of the command.  Multiple lines
19     # are permitted.  The blank line signals the end of this section.
20     # After that, we parses positional and optional arguments.
21     #
22     # The positional argument is enclosed with < and >:
23     #
24     # <PRIVATE_KEY>
25     #
26     # We may describe default behavior without any options by encoding
27     # ( and ):
28     #
29     # (default mode)
30     #
31     # "Options:" is treated specially and produces "OPTIONS" section.
32     # We allow subsection under OPTIONS.  Lines not starting with (, <
33     # and Options: are treated as subsection name and produces section
34     # one level down:
35     #
36     # TLS/SSL:
37     #
38     # The above is an example of subsection.
39     #
40     # The description of arguments must be indented by len(arg_indent)
41     # characters.  The default value should be placed in separate line
42     # and should be start with "Default: " after indentation.
43
44     line = infile.readline().strip()
45     m = re.match(r'^Usage: (.*)', line)
46     if not m:
47         print 'usage line is invalid.  Expected following lines:'
48         print 'Usage: cmdname ...'
49         sys.exit(1)
50     synopsis = m.group(1).split(' ', 1)
51     if len(synopsis) == 2:
52         cmdname, args = synopsis
53     else:
54         cmdname, args = synopsis[0], ''
55
56     description = []
57     for line in infile:
58         line = line.strip()
59         if not line:
60             break
61         description.append(line)
62
63     print '''
64 {cmdname}(1)
65 {cmdnameunderline}
66
67 SYNOPSIS
68 --------
69
70 **{cmdname}** {args}
71
72 DESCRIPTION
73 -----------
74
75 {description}
76 '''.format(cmdname=cmdname, args=args,
77            cmdnameunderline='=' * (len(cmdname) + 3),
78            synopsis=synopsis, description=format_text('\n'.join(description)))
79
80     in_arg = False
81
82     for line in infile:
83         line = line.rstrip()
84
85         if not line.strip() and in_arg:
86             print ''
87             continue
88         if line.startswith('   ') and in_arg:
89             if not line.startswith(arg_indent):
90                 sys.stderr.write('warning: argument description is not indented correctly.  We need {} spaces as indentation.\n'.format(len(arg_indent)))
91             print '{}'.format(format_arg_text(line[len(arg_indent):]))
92             continue
93
94         if in_arg:
95             print ''
96             in_arg = False
97
98         if line == 'Options:':
99             print 'OPTIONS'
100             print '-------'
101             print ''
102             continue
103
104         if line.startswith('  <'):
105             # positional argument
106             m = re.match(r'^(?:\s+)([a-zA-Z0-9-_<>]+)(.*)', line)
107             argname, rest = m.group(1), m.group(2)
108             print '.. describe:: {}'.format(argname)
109             print ''
110             print '{}'.format(format_arg_text(rest.strip()))
111             in_arg = True
112             continue
113
114         if line.startswith('  ('):
115             # positional argument
116             m = re.match(r'^(?:\s+)(\([a-zA-Z0-9-_<> ]+\))(.*)', line)
117             argname, rest = m.group(1), m.group(2)
118             print '.. describe:: {}'.format(argname)
119             print ''
120             print '{}'.format(format_arg_text(rest.strip()))
121             in_arg = True
122             continue
123
124         if line.startswith('  -'):
125             # optional argument
126             m = re.match(
127                 r'^(?:\s+)(-\S+?(?:, -\S+?)*)($| .*)',
128                 line)
129             argname, rest = m.group(1), m.group(2)
130             print '.. option:: {}'.format(argname)
131             print ''
132             rest = rest.strip()
133             if len(rest):
134                 print '{}'.format(format_arg_text(rest))
135             in_arg = True
136             continue
137
138         if not line.startswith(' ') and line.endswith(':'):
139             # subsection
140             subsec = line.strip()[:-1]
141             print '{}'.format(subsec)
142             print '{}'.format('~' * len(subsec))
143             print ''
144             continue
145
146         print line.strip()
147
148 def format_text(text):
149     # escape *
150     if len(text) > len(arg_indent):
151         text = text[:len(arg_indent) + 1] + re.sub(r'\*', r'\*', text[len(arg_indent) + 1:])
152     else:
153         text = re.sub(r'\*', r'\*', text)
154     # markup option reference
155     text = re.sub(r'(^|\s)(-[a-zA-Z0-9-]+)', r'\1:option:`\2`', text)
156     # sphinx does not like markup like ':option:`-f`='.  We need
157     # backslash between ` and =.
158     text = re.sub(r'(:option:`.*?`)(\S)', r'\1\\\2', text)
159     # file path should be italic
160     text = re.sub(r'(^|\s|\'|")(/[^\s\'"]*)', r'\1*\2*', text)
161     return text
162
163 def format_arg_text(text):
164     if text.strip().startswith('Default: '):
165         return '\n    ' + re.sub(r'^(\s*Default: )(.*)$', r'\1``\2``', text)
166     return '    {}'.format(format_text(text))
167
168 if __name__ == '__main__':
169     parser = argparse.ArgumentParser(
170         description='Produces rst document from help output.')
171     parser.add_argument('-i', '--include', metavar='FILE',
172                         help='include content of <FILE> as verbatim.  It should be ReST formatted text.')
173     args = parser.parse_args()
174     help2man(sys.stdin)
175     if args.include:
176         print ''
177         with open(args.include) as f:
178             sys.stdout.write(f.read())