| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 | #!/usr/bin/env python## aria2 - The high speed download utility## Copyright (C) 2013 Tatsuhiro Tsujikawa## This program is free software; you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation; either version 2 of the License, or# (at your option) any later version.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program; if not, write to the Free Software# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA## In addition, as a special exception, the copyright holders give# permission to link the code of portions of this program with the# OpenSSL library under certain conditions as described in each# individual source file, and distribute linked combinations# including the two.# You must obey the GNU General Public License in all respects# for all of the code used other than OpenSSL.  If you modify# file(s) with this exception, you may extend this exception to your# version of the file(s), but you are not obligated to do so.  If you# do not wish to do so, delete this exception statement from your# version.  If you delete this exception statement from all source# files in the program, then also delete it here.## Generates API reference from C++ source code.import re, sys, argparseclass FunctionDoc:    def __init__(self, name, content, domain):        self.name = name        self.content = content        self.domain = domain    def write(self, out):        print '''.. {}:: {}'''.format(self.domain, self.name)        print ''        for line in self.content:            print '    {}'.format(line)class TypedefDoc:    def __init__(self, name, content):        self.name = name        self.content = content    def write(self, out):        print '''.. type:: {}'''.format(self.name)        print ''        for line in self.content:            print '    {}'.format(line)class StructDoc:    def __init__(self, name, content, domain, members, member_domain):        self.name = name        self.content = content        self.domain = domain        self.members = members        self.member_domain = member_domain    def write(self, out):        if self.name:            print '''.. {}:: {}'''.format(self.domain, self.name)            print ''            for line in self.content:                print '    {}'.format(line)            print ''            for name, content in self.members:                print '''    .. {}:: {}'''.format(\                    'function' if name.endswith(')') else self.member_domain,                    name)                print ''                for line in content:                    print '''        {}'''.format(line)            print ''class MacroDoc:    def __init__(self, name, content):        self.name = name        self.content = content    def write(self, out):        print '''.. macro:: {}'''.format(self.name)        print ''        for line in self.content:            print '    {}'.format(line)def make_api_ref(infiles):    macros = []    enums = []    types = []    functions = []    for infile in infiles:        while True:            line = infile.readline()            if not line:                break            elif line == '/**\n':                line = infile.readline()                doctype = line.split()[1]                if doctype == '@function':                    functions.append(process_function('function', infile))                if doctype == '@functypedef':                    types.append(process_function('c:type', infile))                elif doctype == '@typedef':                    types.append(process_typedef(infile))                elif doctype in ['@class', '@struct', '@union']:                    types.append(process_struct(infile))                elif doctype == '@enum':                    enums.append(process_enum(infile))                elif doctype == '@macro':                    macros.append(process_macro(infile))    alldocs = [('Macros', macros),               ('Enums', enums),               ('Types (classes, structs, unions and typedefs)', types),               ('Functions', functions)]    for title, docs in alldocs:        if not docs:            continue        print title        print '-'*len(title)        for doc in docs:            doc.write(sys.stdout)            print ''        print ''def process_macro(infile):    content = read_content(infile)    line = infile.readline()    macro_name = line.split()[1]    return MacroDoc(macro_name, content)def process_enum(infile):    members = []    enum_name = None    content = read_content(infile)    while True:        line = infile.readline()        if not line:            break        elif re.match(r'\s*/\*\*\n', line):            member_content = read_content(infile)            line = infile.readline()            items = line.split()            member_name = items[0].rstrip(',')            if len(items) >= 3:                member_content.insert(0, '(``{}``) '\                                          .format(items[2].rstrip(',')))            members.append((member_name, member_content))        elif line.startswith('}'):            if not enum_name:                enum_name = line.rstrip().split()[1]            enum_name = re.sub(r';$', '', enum_name)            break        elif not enum_name:            m = re.match(r'^\s*enum\s+([\S]+)\s*{\s*', line)            if m:                enum_name = m.group(1)    return StructDoc(enum_name, content, 'type', members, 'c:macro')def process_struct(infile):    members = []    domain = 'type'    struct_name = None    content = read_content(infile)    while True:        line = infile.readline()        if not line:            break        elif re.match(r'\s*/\*\*\n', line):            member_content = read_content(infile)            line = infile.readline()            member_name = line.rstrip().rstrip(';')            member_name = re.sub(r'\)\s*=\s*0', ')', member_name)            member_name = re.sub(r' virtual ', '', member_name)            members.append((member_name, member_content))        elif line.startswith('}') or\                (line.startswith('typedef ') and line.endswith(';\n')):            if not struct_name:                if line.startswith('}'):                    index = 1                else:                    index = 3                struct_name = line.rstrip().split()[index]            struct_name = re.sub(r';$', '', struct_name)            break        elif not struct_name:            m = re.match(r'^\s*(struct|class)\s+([\S]+)\s*(?:{|;)', line)            if m:                domain = m.group(1)                if domain == 'struct':                    domain = 'type'                struct_name = m.group(2)                if line.endswith(';\n'):                    break    return StructDoc(struct_name, content, domain, members, 'member')def process_function(domain, infile):    content = read_content(infile)    func_proto = []    while True:        line = infile.readline()        if not line:            break        elif line == '\n':            break        else:            func_proto.append(line)    func_proto = ''.join(func_proto)    func_proto = re.sub(r';\n$', '', func_proto)    func_proto = re.sub(r'\s+', ' ', func_proto)    return FunctionDoc(func_proto, content, domain)def process_typedef(infile):    content = read_content(infile)    lines = []    while True:        line = infile.readline()        if not line:            break        elif line == '\n':            break        else:            lines.append(line)    typedef = ''.join(lines)    typedef = re.sub(r';\n$', '', typedef)    typedef = re.sub(r'\s+', ' ', typedef)    return TypedefDoc(typedef.split()[-1], content)def read_content(infile):    content = []    while True:        line = infile.readline()        if not line:            break        if re.match(r'\s*\*/\n', line):            break        else:            content.append(transform_content(line.rstrip()))    return contentdef arg_repl(matchobj):    return '*{}*'.format(matchobj.group(1).replace('*', '\\*'))def transform_content(content):    content = re.sub(r'^\s+\* ?', '', content)    content = re.sub(r'\|([^\s|]+)\|', arg_repl, content)    content = re.sub(r':enum:', ':macro:', content)    return contentif __name__ == '__main__':    parser = argparse.ArgumentParser(description="Generate API reference")    parser.add_argument('--header', type=argparse.FileType('rb', 0),                        help='header inserted at the top of the page')    parser.add_argument('files', nargs='+', type=argparse.FileType('rb', 0),                        help='source file')    args = parser.parse_args()    if args.header:        print args.header.read()    for infile in args.files:        make_api_ref(args.files)
 |