1#!/usr/bin/env python 2# 3# aria2 - The high speed download utility 4# 5# Copyright (C) 2013 Tatsuhiro Tsujikawa 6# 7# This program is free software; you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 2 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program; if not, write to the Free Software 19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20# 21# In addition, as a special exception, the copyright holders give 22# permission to link the code of portions of this program with the 23# OpenSSL library under certain conditions as described in each 24# individual source file, and distribute linked combinations 25# including the two. 26# You must obey the GNU General Public License in all respects 27# for all of the code used other than OpenSSL. If you modify 28# file(s) with this exception, you may extend this exception to your 29# version of the file(s), but you are not obligated to do so. If you 30# do not wish to do so, delete this exception statement from your 31# version. If you delete this exception statement from all source 32# files in the program, then also delete it here. 33# 34# Generates API reference from C++ source code. 35from __future__ import print_function 36import re, sys, argparse 37 38class FunctionDoc: 39 def __init__(self, name, content, domain): 40 self.name = name 41 self.content = content 42 self.domain = domain 43 44 def write(self, out): 45 print('''.. {}:: {}'''.format(self.domain, self.name)) 46 print() 47 for line in self.content: 48 print(' {}'.format(line)) 49 50class TypedefDoc: 51 def __init__(self, name, content): 52 self.name = name 53 self.content = content 54 55 def write(self, out): 56 print('''.. type:: {}'''.format(self.name)) 57 print() 58 for line in self.content: 59 print(' {}'.format(line)) 60 61class StructDoc: 62 def __init__(self, name, content, domain, members, member_domain): 63 self.name = name 64 self.content = content 65 self.domain = domain 66 self.members = members 67 self.member_domain = member_domain 68 69 def write(self, out): 70 if self.name: 71 print('''.. {}:: {}'''.format(self.domain, self.name)) 72 print() 73 for line in self.content: 74 print(' {}'.format(line)) 75 print() 76 for name, content in self.members: 77 name = name.strip() 78 # For function (e.g., int foo()) 79 m = re.match(r'(.+)\s+([^ ]+\(.*)', name) 80 if not m: 81 # For variable (e.g., bool a) 82 m = re.match(r'(.+)\s+([^ ]+)', name) 83 if m: 84 print(''' .. {}:: {} {}::{}'''.format( 85 'function' if name.endswith(')') else self.member_domain, 86 m.group(1), 87 self.name, 88 m.group(2))) 89 else: 90 if name.endswith(')'): 91 # For function, without return type, like 92 # constructor 93 print(''' .. {}:: {}::{}'''.format( 94 'function' if name.endswith(')') else self.member_domain, 95 self.name, name)) 96 else: 97 # enum 98 print(''' .. {}:: {}'''.format( 99 'function' if name.endswith(')') else self.member_domain, 100 name)) 101 print() 102 for line in content: 103 print(''' {}'''.format(line)) 104 print() 105 106class MacroDoc: 107 def __init__(self, name, content): 108 self.name = name 109 self.content = content 110 111 def write(self, out): 112 print('''.. macro:: {}'''.format(self.name)) 113 print() 114 for line in self.content: 115 print(' {}'.format(line)) 116 117def make_api_ref(infiles): 118 macros = [] 119 enums = [] 120 types = [] 121 functions = [] 122 for infile in infiles: 123 while True: 124 line = infile.readline() 125 if not line: 126 break 127 elif line == '/**\n': 128 line = infile.readline() 129 doctype = line.split()[1] 130 if doctype == '@function': 131 functions.append(process_function('function', infile)) 132 if doctype == '@functypedef': 133 types.append(process_function('type', infile)) 134 elif doctype == '@typedef': 135 types.append(process_typedef(infile)) 136 elif doctype in ['@class', '@struct', '@union']: 137 types.append(process_struct(infile)) 138 elif doctype == '@enum': 139 enums.append(process_enum(infile)) 140 elif doctype == '@macro': 141 macros.append(process_macro(infile)) 142 alldocs = [('Macros', macros), 143 ('Enums', enums), 144 ('Types (classes, structs, unions and typedefs)', types), 145 ('Functions', functions)] 146 for title, docs in alldocs: 147 if not docs: 148 continue 149 print(title) 150 print('-'*len(title)) 151 for doc in docs: 152 doc.write(sys.stdout) 153 print() 154 print() 155 156def process_macro(infile): 157 content = read_content(infile) 158 line = infile.readline() 159 macro_name = line.split()[1] 160 return MacroDoc(macro_name, content) 161 162def process_enum(infile): 163 members = [] 164 enum_name = None 165 content = read_content(infile) 166 while True: 167 line = infile.readline() 168 if not line: 169 break 170 elif re.match(r'\s*/\*\*\n', line): 171 member_content = read_content(infile) 172 line = infile.readline() 173 items = line.split() 174 member_name = items[0].rstrip(',') 175 if len(items) >= 3: 176 member_content.insert(0, '(``{}``) '\ 177 .format(items[2].rstrip(','))) 178 members.append((member_name, member_content)) 179 elif line.startswith('}'): 180 if not enum_name: 181 enum_name = line.rstrip().split()[1] 182 enum_name = re.sub(r';$', '', enum_name) 183 break 184 elif not enum_name: 185 m = re.match(r'^\s*enum\s+([\S]+)\s*{\s*', line) 186 if m: 187 enum_name = m.group(1) 188 return StructDoc(enum_name, content, 'type', members, 'c:macro') 189 190def process_struct(infile): 191 members = [] 192 domain = 'type' 193 struct_name = None 194 content = read_content(infile) 195 while True: 196 line = infile.readline() 197 if not line: 198 break 199 elif re.match(r'\s*/\*\*\n', line): 200 member_content = read_content(infile) 201 line = infile.readline() 202 member_name = line.rstrip().rstrip(';') 203 member_name = re.sub(r'\)\s*=\s*0', ')', member_name) 204 member_name = re.sub(r' virtual ', '', member_name) 205 members.append((member_name, member_content)) 206 elif line.startswith('}') or\ 207 (line.startswith('typedef ') and line.endswith(';\n')): 208 if not struct_name: 209 if line.startswith('}'): 210 index = 1 211 else: 212 index = 3 213 struct_name = line.rstrip().split()[index] 214 struct_name = re.sub(r';$', '', struct_name) 215 break 216 elif not struct_name: 217 m = re.match(r'^\s*(struct|class)\s+([\S]+)\s*(?:{|;)', line) 218 if m: 219 domain = m.group(1) 220 if domain == 'struct': 221 domain = 'type' 222 struct_name = m.group(2) 223 if line.endswith(';\n'): 224 break 225 return StructDoc(struct_name, content, domain, members, 'member') 226 227def process_function(domain, infile): 228 content = read_content(infile) 229 func_proto = [] 230 while True: 231 line = infile.readline() 232 if not line: 233 break 234 elif line == '\n': 235 break 236 else: 237 func_proto.append(line) 238 func_proto = ''.join(func_proto) 239 func_proto = re.sub(r';\n$', '', func_proto) 240 func_proto = re.sub(r'\s+', ' ', func_proto) 241 func_proto = re.sub(r'typedef ', '', func_proto) 242 return FunctionDoc(func_proto, content, domain) 243 244def process_typedef(infile): 245 content = read_content(infile) 246 lines = [] 247 while True: 248 line = infile.readline() 249 if not line: 250 break 251 elif line == '\n': 252 break 253 else: 254 lines.append(line) 255 typedef = ''.join(lines) 256 typedef = re.sub(r';\n$', '', typedef) 257 typedef = re.sub(r'\s+', ' ', typedef) 258 typedef = re.sub(r'typedef ', '', typedef) 259 return TypedefDoc(typedef.split()[-1], content) 260 261def read_content(infile): 262 content = [] 263 while True: 264 line = infile.readline() 265 if not line: 266 break 267 if re.match(r'\s*\*/\n', line): 268 break 269 else: 270 content.append(transform_content(line.rstrip())) 271 return content 272 273def arg_repl(matchobj): 274 return '*{}*'.format(matchobj.group(1).replace('*', '\\*')) 275 276def transform_content(content): 277 content = re.sub(r'^\s+\* ?', '', content) 278 content = re.sub(r'\|([^\s|]+)\|', arg_repl, content) 279 content = re.sub(r':enum:', ':macro:', content) 280 return content 281 282if __name__ == '__main__': 283 parser = argparse.ArgumentParser(description="Generate API reference") 284 parser.add_argument('--header', type=argparse.FileType('rb', 0), 285 help='header inserted at the top of the page') 286 parser.add_argument('files', nargs='+', type=argparse.FileType('rb', 0), 287 help='source file') 288 args = parser.parse_args() 289 if args.header: 290 print(args.header.read()) 291 for infile in args.files: 292 make_api_ref(args.files) 293