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

#!/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")