You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
7.3 KiB
187 lines
7.3 KiB
#!/usr/bin/python3 |
|
|
|
import sys |
|
import os |
|
import subprocess |
|
from pycparser import parse_file, c_ast, CParser |
|
import re |
|
|
|
def read_filelist(filename='filelist.txt'): |
|
with open(filename, 'r') as f: |
|
return [line.strip() for line in f if line.strip()] |
|
|
|
def preprocess_file(filename): |
|
# Препроцессинг с помощью cpp (GCC preprocessor) |
|
try: |
|
preprocessed = subprocess.check_output(['cpp', '-nostdinc', filename]).decode('utf-8') |
|
return preprocessed.splitlines() |
|
except subprocess.CalledProcessError as e: |
|
print(f"Error preprocessing {filename}: {e}") |
|
return None |
|
|
|
def collect_comments_and_code(lines): |
|
# Собираем комментарии и очищаем код для парсинга (pycparser игнорирует комментарии) |
|
comments = {} |
|
clean_lines = [] |
|
in_multi = False |
|
current_comment = [] |
|
line_num = 0 |
|
for line in lines: |
|
line_num += 1 |
|
stripped = line.strip() |
|
if in_multi: |
|
end = line.find('*/') |
|
if end != -1: |
|
current_comment.append(line[:end+2]) |
|
in_multi = False |
|
# Добавляем остаток строки как код |
|
rest = line[end+2:] |
|
if rest.strip(): |
|
clean_lines.append(rest) |
|
comments[line_num] = '\n'.join(current_comment) |
|
current_comment = [] |
|
else: |
|
current_comment.append(line) |
|
else: |
|
if stripped.startswith('//'): |
|
comments[line_num] = line |
|
elif stripped.startswith('/*'): |
|
if '*/' in stripped: |
|
comments[line_num] = line |
|
else: |
|
in_multi = True |
|
current_comment = [line] |
|
else: |
|
clean_lines.append(line) |
|
return comments, '\n'.join(clean_lines) |
|
|
|
class FuncVisitor(c_ast.NodeVisitor): |
|
def __init__(self, comments): |
|
self.functions = [] |
|
self.structs = [] |
|
self.comments = comments |
|
|
|
def visit_FuncDef(self, node): |
|
# Извлекаем функцию |
|
ret = node.decl.type.type.type.names[0] if hasattr(node.decl.type.type, 'type') else 'void' |
|
name = node.decl.name |
|
args = ', '.join([p.type.type.names[0] + ' ' + p.name for p in node.decl.type.args.params]) if node.decl.type.args else '' |
|
pre_comments = [] |
|
# Собираем комментарии перед функцией (приблизительно по coord) |
|
if node.coord: |
|
line = node.coord.line |
|
for l in range(line-1, 0, -1): |
|
if l in self.comments and (self.comments[l].startswith('//') or self.comments[l].startswith('/*')): |
|
pre_comments.insert(0, self.comments[l]) |
|
else: |
|
break |
|
body_lines = len(node.body.block_items) if node.body else 0 # Приблизительное число строк |
|
self.functions.append({ |
|
'type': 'function', 'name': name, 'args': args, 'ret': ret, |
|
'pre': pre_comments, 'line_count': body_lines + 2, # + декларация и скобки |
|
'start': node.coord.line - 1 if node.coord else 0, |
|
'end': node.coord.line + body_lines if node.coord else 0, |
|
'node': node |
|
}) |
|
self.generic_visit(node) |
|
|
|
def visit_Struct(self, node): |
|
# Извлекаем структуру |
|
name = node.name |
|
pre_comments = [] |
|
if node.coord: |
|
line = node.coord.line |
|
for l in range(line-1, 0, -1): |
|
if l in self.comments and (self.comments[l].startswith('//') or self.comments[l].startswith('/*')): |
|
pre_comments.insert(0, self.comments[l]) |
|
else: |
|
break |
|
line_count = len(node.decls) + 2 if node.decls else 2 |
|
self.structs.append({ |
|
'type': 'struct', 'name': name, |
|
'pre': pre_comments, 'line_count': line_count, |
|
'start': node.coord.line - 1 if node.coord else 0, |
|
'end': node.coord.line + line_count if node.coord else 0, |
|
'node': node |
|
}) |
|
self.generic_visit(node) |
|
|
|
def parse_c_file(filename, comments): |
|
ast = parse_file(filename, use_cpp=False) # Уже препроцессировано |
|
visitor = FuncVisitor(comments) |
|
visitor.visit(ast) |
|
return visitor.functions, visitor.structs |
|
|
|
if __name__ == "__main__": |
|
files = read_filelist() |
|
project_functions = {} |
|
project_structs = {} |
|
contents = {} # Оригинальный код для вывода |
|
for f in files: |
|
contents[f] = open(f, 'r').read().splitlines() |
|
pre_lines = preprocess_file(f) |
|
if pre_lines is None: |
|
continue |
|
comments, clean_code = collect_comments_and_code(pre_lines) |
|
# Записываем очищенный код во временный файл для pycparser |
|
tmp_file = f + '.pre' |
|
with open(tmp_file, 'w') as tf: |
|
tf.write(clean_code) |
|
try: |
|
funcs, structs = parse_c_file(tmp_file, comments) |
|
project_functions[f] = funcs |
|
project_structs[f] = structs |
|
except Exception as e: |
|
print(f"Parse error in {f}: {e}") |
|
finally: |
|
os.remove(tmp_file) |
|
|
|
if len(sys.argv) < 2: |
|
print("Usage: python c_util.py toc | func <file> <funcname> | struct <file> <structname>") |
|
sys.exit(1) |
|
cmd = sys.argv[1] |
|
if cmd == 'toc': |
|
for f in files: |
|
print(f"File: {f}") |
|
for func in project_functions.get(f, []): |
|
print(f"Function: {func['ret']} {func['name']}({func['args']}) - {func['line_count']} lines") |
|
for strct in project_structs.get(f, []): |
|
print(f"Struct: {strct['name']} - {strct['line_count']} lines") # Добавил вывод структур в TOC |
|
elif cmd == 'func': |
|
if len(sys.argv) < 4: |
|
print("Usage: python c_util.py func <file> <funcname>") |
|
sys.exit(1) |
|
file = sys.argv[2] |
|
funcname = sys.argv[3] |
|
found = False |
|
if file in project_functions: |
|
for func in project_functions[file]: |
|
if func['name'] == funcname: |
|
lines = contents[file] |
|
pre = func['pre'] |
|
body = lines[func['start']:func['end']] |
|
print('\n'.join(pre + body)) |
|
found = True |
|
break |
|
if not found: |
|
print("Function not found") |
|
elif cmd == 'struct': |
|
if len(sys.argv) < 4: |
|
print("Usage: python c_util.py struct <file> <structname>") |
|
sys.exit(1) |
|
file = sys.argv[2] |
|
structname = sys.argv[3] |
|
found = False |
|
if file in project_structs: |
|
for strct in project_structs[file]: |
|
if strct['name'] == structname: |
|
lines = contents[file] |
|
pre = strct['pre'] |
|
body = lines[strct['start']:strct['end']] |
|
print('\n'.join(pre + body)) |
|
found = True |
|
break |
|
if not found: |
|
print("Struct not found") |
|
else: |
|
print("Unknown command")
|
|
|