|  | #!/usr/bin/env python3 | 
|  | # | 
|  | # Copyright (c) 2017 Intel Corporation | 
|  | # | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | """ | 
|  | gperf C file post-processor | 
|  |  | 
|  | We use gperf to build up a perfect hashtable of pointer values. The way gperf | 
|  | does this is to create a table 'wordlist' indexed by a string representation | 
|  | of a pointer address, and then doing memcmp() on a string passed in for | 
|  | comparison | 
|  |  | 
|  | We are exclusively working with 4-byte pointer values. This script adjusts | 
|  | the generated code so that we work with pointers directly and not strings. | 
|  | This saves a considerable amount of space. | 
|  | """ | 
|  |  | 
|  | import sys | 
|  | import argparse | 
|  | import os | 
|  | import re | 
|  | from packaging import version | 
|  |  | 
|  | # --- debug stuff --- | 
|  |  | 
|  | def debug(text): | 
|  | if not args.verbose: | 
|  | return | 
|  | sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") | 
|  |  | 
|  |  | 
|  | def error(text): | 
|  | sys.exit(os.path.basename(sys.argv[0]) + " ERROR: " + text) | 
|  |  | 
|  |  | 
|  | def warn(text): | 
|  | sys.stdout.write( | 
|  | os.path.basename( | 
|  | sys.argv[0]) + | 
|  | " WARNING: " + | 
|  | text + | 
|  | "\n") | 
|  |  | 
|  |  | 
|  | def reformat_str(match_obj): | 
|  | addr_str = match_obj.group(0) | 
|  |  | 
|  | # Nip quotes | 
|  | addr_str = addr_str[1:-1] | 
|  | addr_vals = [0, 0, 0, 0, 0, 0, 0 , 0] | 
|  | ctr = 7 | 
|  | i = 0 | 
|  |  | 
|  | while True: | 
|  | if i >= len(addr_str): | 
|  | break | 
|  |  | 
|  | if addr_str[i] == "\\": | 
|  | if addr_str[i + 1].isdigit(): | 
|  | # Octal escape sequence | 
|  | val_str = addr_str[i + 1:i + 4] | 
|  | addr_vals[ctr] = int(val_str, 8) | 
|  | i += 4 | 
|  | else: | 
|  | # Char value that had to be escaped by C string rules | 
|  | addr_vals[ctr] = ord(addr_str[i + 1]) | 
|  | i += 2 | 
|  |  | 
|  | else: | 
|  | addr_vals[ctr] = ord(addr_str[i]) | 
|  | i += 1 | 
|  |  | 
|  | ctr -= 1 | 
|  |  | 
|  | return "(char *)0x%02x%02x%02x%02x%02x%02x%02x%02x" % tuple(addr_vals) | 
|  |  | 
|  |  | 
|  | def process_line(line, fp): | 
|  | if line.startswith("#"): | 
|  | fp.write(line) | 
|  | return | 
|  |  | 
|  | # Set the lookup function to static inline so it gets rolled into | 
|  | # z_object_find(), nothing else will use it | 
|  | if re.search(args.pattern + " [*]$", line): | 
|  | fp.write("static inline " + line) | 
|  | return | 
|  |  | 
|  | m = re.search("gperf version (.*) [*][/]$", line) | 
|  | if m: | 
|  | v = version.parse(m.groups()[0]) | 
|  | v_lo = version.parse("3.0") | 
|  | v_hi = version.parse("3.1") | 
|  | if (v < v_lo or v > v_hi): | 
|  | warn("gperf %s is not tested, versions %s through %s supported" % | 
|  | (v, v_lo, v_hi)) | 
|  |  | 
|  | # Replace length lookups with constant len since we're always | 
|  | # looking at pointers | 
|  | line = re.sub(r'lengthtable\[key\]', r'sizeof(void *)', line) | 
|  |  | 
|  | # Empty wordlist entries to have NULLs instead of "" | 
|  | line = re.sub(r'[{]["]["][}]', r'{}', line) | 
|  |  | 
|  | # Suppress a compiler warning since this table is no longer necessary | 
|  | line = re.sub(r'static unsigned char lengthtable', | 
|  | r'static unsigned char __unused lengthtable', line) | 
|  |  | 
|  | # drop all use of register keyword, let compiler figure that out, | 
|  | # we have to do this since we change stuff to take the address of some | 
|  | # parameters | 
|  | line = re.sub(r'register', r'', line) | 
|  |  | 
|  | # Hashing the address of the string | 
|  | line = re.sub(r"hash [(]str, len[)]", | 
|  | r"hash((const char *)&str, len)", line) | 
|  |  | 
|  | # Just compare pointers directly instead of using memcmp | 
|  | if re.search("if [(][*]str", line): | 
|  | fp.write("            if (str == s)\n") | 
|  | return | 
|  |  | 
|  | # Take the strings with the binary information for the pointer values, | 
|  | # and just turn them into pointers | 
|  | line = re.sub(r'["].*["]', reformat_str, line) | 
|  |  | 
|  | fp.write(line) | 
|  |  | 
|  |  | 
|  | def parse_args(): | 
|  | global args | 
|  |  | 
|  | parser = argparse.ArgumentParser( | 
|  | description=__doc__, | 
|  | formatter_class=argparse.RawDescriptionHelpFormatter, | 
|  | allow_abbrev=False) | 
|  |  | 
|  | parser.add_argument("-i", "--input", required=True, | 
|  | help="Input C file from gperf") | 
|  | parser.add_argument("-o", "--output", required=True, | 
|  | help="Output C file with processing done") | 
|  | parser.add_argument("-p", "--pattern", required=True, | 
|  | help="Search pattern for objects") | 
|  | parser.add_argument("-v", "--verbose", action="store_true", | 
|  | help="Print extra debugging information") | 
|  | args = parser.parse_args() | 
|  | if "VERBOSE" in os.environ: | 
|  | args.verbose = 1 | 
|  |  | 
|  | def main(): | 
|  | parse_args() | 
|  |  | 
|  | with open(args.input, "r") as in_fp, open(args.output, "w") as out_fp: | 
|  | for line in in_fp.readlines(): | 
|  | process_line(line, out_fp) | 
|  |  | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | main() |