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