1#!/usr/bin/env python3 2# 3# american fuzzy lop++ - custom code formatter 4# -------------------------------------------- 5# 6# Written and maintained by Andrea Fioraldi <[email protected]> 7# 8# Copyright 2015, 2016, 2017 Google Inc. All rights reserved. 9# Copyright 2019-2023 AFLplusplus Project. All rights reserved. 10# 11# Licensed under the Apache License, Version 2.0 (the "License"); 12# you may not use this file except in compliance with the License. 13# You may obtain a copy of the License at: 14# 15# http://www.apache.org/licenses/LICENSE-2.0 16# 17 18import subprocess 19import sys 20import os 21# import re # TODO: for future use 22import shutil 23import importlib.metadata 24 25# string_re = re.compile('(\\"(\\\\.|[^"\\\\])*\\")') # TODO: for future use 26 27CURRENT_LLVM = os.getenv('LLVM_VERSION', 17) 28CLANG_FORMAT_BIN = os.getenv("CLANG_FORMAT_BIN", "") 29 30 31def check_clang_format_pip_version(): 32 """ 33 Check if the correct version of clang-format is installed via pip. 34 35 Returns: 36 bool: True if the correct version of clang-format is installed, 37 False otherwise. 38 """ 39 # Check if clang-format is installed 40 if importlib.util.find_spec('clang_format'): 41 # Check if the installed version is the expected LLVM version 42 if importlib.metadata.version('clang-format')\ 43 .startswith(str(CURRENT_LLVM)+'.'): 44 return True 45 else: 46 # Return False, because the clang-format version does not match 47 return False 48 else: 49 # If the 'clang_format' package isn't installed, return False 50 return False 51 52 53with open(".clang-format") as f: 54 fmt = f.read() 55 56 57CLANG_FORMAT_PIP = check_clang_format_pip_version() 58 59if shutil.which(CLANG_FORMAT_BIN) is None: 60 CLANG_FORMAT_BIN = f"clang-format-{CURRENT_LLVM}" 61 62if shutil.which(CLANG_FORMAT_BIN) is None \ 63 and CLANG_FORMAT_PIP is False: 64 print(f"[!] clang-format-{CURRENT_LLVM} is needed. Aborted.") 65 print(f"Run `pip3 install \"clang-format=={CURRENT_LLVM}.*\"` \ 66to install via pip.") 67 exit(1) 68 69if CLANG_FORMAT_PIP: 70 CLANG_FORMAT_BIN = shutil.which("clang-format") 71 72COLUMN_LIMIT = 80 73for line in fmt.split("\n"): 74 line = line.split(":") 75 if line[0].strip() == "ColumnLimit": 76 COLUMN_LIMIT = int(line[1].strip()) 77 78 79def custom_format(filename): 80 p = subprocess.Popen([CLANG_FORMAT_BIN, filename], stdout=subprocess.PIPE) 81 src, _ = p.communicate() 82 src = str(src, "utf-8") 83 84 in_define = False 85 last_line = None 86 out = "" 87 88 for line in src.split("\n"): 89 if line.lstrip().startswith("#"): 90 if line[line.find("#") + 1:].lstrip().startswith("define"): 91 in_define = True 92 93 if ( 94 "/*" in line 95 and not line.strip().startswith("/*") 96 and line.endswith("*/") 97 and len(line) < (COLUMN_LIMIT - 2) 98 ): 99 cmt_start = line.rfind("/*") 100 line = ( 101 line[:cmt_start] 102 + " " * (COLUMN_LIMIT - 2 - len(line)) 103 + line[cmt_start:] 104 ) 105 106 define_padding = 0 107 if last_line is not None and in_define and last_line.endswith("\\"): 108 last_line = last_line[:-1] 109 define_padding = max(0, len(last_line[last_line.rfind("\n") + 1:])) 110 111 if ( 112 last_line is not None 113 and last_line.strip().endswith("{") 114 and line.strip() != "" 115 ): 116 line = (" " * define_padding + "\\" if in_define else "") + "\n" + line 117 elif ( 118 last_line is not None 119 and last_line.strip().startswith("}") 120 and line.strip() != "" 121 ): 122 line = (" " * define_padding + "\\" if in_define else "") + "\n" + line 123 elif ( 124 line.strip().startswith("}") 125 and last_line is not None 126 and last_line.strip() != "" 127 ): 128 line = (" " * define_padding + "\\" if in_define else "") + "\n" + line 129 130 if not line.endswith("\\"): 131 in_define = False 132 133 out += line + "\n" 134 last_line = line 135 136 return out 137 138 139args = sys.argv[1:] 140if len(args) == 0: 141 print("Usage: ./format.py [-i] <filename>") 142 print() 143 print(" The -i option, if specified, let the script to modify in-place") 144 print(" the source files. By default the results are written to stdout.") 145 print() 146 exit(1) 147 148in_place = False 149if args[0] == "-i": 150 in_place = True 151 args = args[1:] 152 153for filename in args: 154 code = custom_format(filename) 155 if in_place: 156 with open(filename, "w") as f: 157 f.write(code) 158 else: 159 print(code) 160