|  | #!/usr/bin/env python3 | 
|  |  | 
|  | # Copyright (c) 2023 Baumer (www.baumer.com) | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | """This script converting the Zephyr coding guideline rst file to a output file, | 
|  | or print the output to the console. Which than can be used by a tool which | 
|  | needs to have that information in a specific format (e.g. for cppcheck). | 
|  | Or simply use the rule list to generate a filter to suppress all other rules | 
|  | used by default from such a tool. | 
|  | """ | 
|  |  | 
|  | import sys | 
|  | import re | 
|  | import argparse | 
|  | from pathlib import Path | 
|  |  | 
|  | class RuleFormatter: | 
|  | """ | 
|  | Base class for the different output formats | 
|  | """ | 
|  | def table_start_print(self, outputfile): | 
|  | pass | 
|  | def severity_print(self, outputfile, guideline_number, severity): | 
|  | pass | 
|  | def description_print(self, outputfile, guideline_number, description): | 
|  | pass | 
|  | def closing_print(self, outputfile): | 
|  | pass | 
|  |  | 
|  | class CppCheckFormatter(RuleFormatter): | 
|  | """ | 
|  | Formatter class to print the rules in a format which can be used by cppcheck | 
|  | """ | 
|  | def table_start_print(self, outputfile): | 
|  | # Start search by cppcheck misra addon | 
|  | print('Appendix A Summary of guidelines', file=outputfile) | 
|  |  | 
|  | def severity_print(self, outputfile, guideline_number, severity): | 
|  | print('Rule ' + guideline_number + ' ' + severity, file=outputfile) | 
|  |  | 
|  | def description_print(self, outputfile, guideline_number, description): | 
|  | print(description + '(Misra rule ' + guideline_number + ')', file=outputfile) | 
|  |  | 
|  | def closing_print(self, outputfile): | 
|  | # Make cppcheck happy by starting the appendix | 
|  | print('Appendix B', file=outputfile) | 
|  | print('', file=outputfile) | 
|  |  | 
|  | def convert_guidelines(args): | 
|  | inputfile = args.input | 
|  | outputfile = sys.stdout | 
|  | formatter = None | 
|  |  | 
|  | # If the output is not empty, open the given file for writing | 
|  | if args.output is not None: | 
|  | outputfile = open(args.output, "w") | 
|  |  | 
|  | try: | 
|  | file_stream = open(inputfile, 'rt', errors='ignore') | 
|  | except Exception: | 
|  | print('Error opening ' + inputfile +'.') | 
|  | sys.exit() | 
|  |  | 
|  | # Set formatter according to the used format | 
|  | if args.format == 'cppcheck': | 
|  | formatter = CppCheckFormatter() | 
|  |  | 
|  | # Search for table named Main rules | 
|  | pattern_table_start = re.compile(r'.*list-table:: Main rules') | 
|  | # Each Rule is a new table column so start with '[tab]* -  Rule' | 
|  | # Ignore directives here | 
|  | pattern_new_line = re.compile(r'^    \* -  Rule ([0-9]+.[0-9]+).*$') | 
|  | # Each table column start with '[tab]-  ' | 
|  | pattern_new_col = re.compile(r'^      -  (.*)$') | 
|  |  | 
|  | table_start = False | 
|  | guideline_number = '' | 
|  | guideline_state  = 0 | 
|  | guideline_list = [] | 
|  |  | 
|  | for line in file_stream: | 
|  |  | 
|  | line = line.replace('\r', '').replace('\n', '') | 
|  |  | 
|  | # Done if we find the Additional rules table start | 
|  | if line.find('Additional rules') >= 0: | 
|  | break | 
|  |  | 
|  | if len(line) == 0: | 
|  | continue | 
|  |  | 
|  | if not table_start: | 
|  | res = pattern_table_start.match(line) | 
|  | if res: | 
|  | table_start = True | 
|  | formatter.table_start_print(outputfile) | 
|  | continue | 
|  |  | 
|  | res = pattern_new_line.match(line) | 
|  | if res: | 
|  | guideline_state = "severity" | 
|  | guideline_number = res.group(1) | 
|  | guideline_list.append(guideline_number) | 
|  | continue | 
|  | elif guideline_number == '': | 
|  | continue | 
|  |  | 
|  | res = pattern_new_col.match(line) | 
|  | if res: | 
|  | if guideline_state == "severity": | 
|  | # Severity | 
|  | formatter.severity_print(outputfile, guideline_number, res.group(1)) | 
|  | guideline_state = "description" | 
|  | continue | 
|  | if guideline_state == "description": | 
|  | # Description | 
|  | formatter.description_print(outputfile, guideline_number, res.group(1)) | 
|  | guideline_state = "None" | 
|  | # We stop here for now, we do not handle the CERT C col | 
|  | guideline_number = '' | 
|  | continue | 
|  |  | 
|  | formatter.closing_print(outputfile) | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | supported_formats = ['cppcheck'] | 
|  |  | 
|  | parser = argparse.ArgumentParser(allow_abbrev=False) | 
|  | parser.add_argument( | 
|  | "-i", "--input", metavar="RST_FILE", type=Path, required=True, | 
|  | help="Path to rst input source file, where the guidelines are written down." | 
|  | ) | 
|  | parser.add_argument( | 
|  | "-f", "--format", metavar="FORMAT", choices=supported_formats, required=True, | 
|  | help="Format to convert guidlines to. Supported formats are: " + str(supported_formats) | 
|  | ) | 
|  | parser.add_argument( | 
|  | "-o", "--output", metavar="OUTPUT_FILE", type=Path, required=False, | 
|  | help="Path to output file, where the converted guidelines are written to. If outputfile is not specified, print to stdout." | 
|  | ) | 
|  | args = parser.parse_args() | 
|  |  | 
|  | convert_guidelines(args) |