Vernon Mauery | 0d0cd16 | 2020-01-31 10:04:10 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | import re, sys, os.path |
| 4 | |
| 5 | def usage(): |
| 6 | sys.stderr.write("Usage: $0 whitelist-config-in whitelist-header-out\n") |
| 7 | sys.stderr.write(" Reads in whitelist config, sorting the contents\n") |
| 8 | sys.stderr.write(" and outputs a header file\n") |
| 9 | sys.exit(-1) |
| 10 | |
| 11 | class Error(Exception): |
| 12 | pass |
| 13 | |
| 14 | class DuplicateEntry(Error): |
| 15 | def __init__(self, e): |
| 16 | super(Error, self).__init__( |
| 17 | "Multiple entries with matching netfn/cmd found ({})".format(e)) |
| 18 | |
| 19 | class ParseError(Error): |
| 20 | def __init__(self, d): |
| 21 | super(Error, self).__init__("Parse error at: '{}'".format(d)) |
| 22 | |
| 23 | class entry: |
| 24 | linere = re.compile( |
| 25 | r'(0x[0-9a-f]{2}):(0x[0-9a-f]{2})((:(0x[0-9a-f]{4}))?)\s*((//\s*(.*))?)', |
| 26 | re.I |
| 27 | ) |
| 28 | def __init__(self, data): |
| 29 | # parse data line into values: |
| 30 | # type 1, two values: netfn, cmd |
| 31 | # type 2, three values: netfn, cmd, channels |
| 32 | try: |
| 33 | m = self.linere.fullmatch(data).groups() |
| 34 | except: |
| 35 | raise ParseError(data) |
| 36 | self.netfn = int(m[0], 16) |
| 37 | self.cmd = int(m[1], 16) |
| 38 | if m[4] is not None: |
| 39 | self.channels = int(m[4], 16) |
| 40 | else: |
| 41 | # if no channel was provided, default to previous behavior, which |
| 42 | # is allow all interfaces, including the system interface (ch 15) |
| 43 | self.channels = 0xffff |
| 44 | if m[6] is not None: |
| 45 | self.comment = "// " + m[7] |
| 46 | else: |
| 47 | self.comment = "//" |
| 48 | def __str__(self): |
| 49 | return " ".join([ '{', |
| 50 | "0x{0.netfn:02x},".format(self), |
| 51 | "0x{0.cmd:02x},".format(self), |
| 52 | "0x{0.channels:04x}".format(self), |
| 53 | "},", |
| 54 | "{0.comment}".format(self), |
| 55 | ]) |
| 56 | def __lt__(self, other): |
| 57 | if self.netfn == other.netfn: |
| 58 | return self.cmd < other.cmd |
| 59 | return self.netfn < other.netfn |
| 60 | def match(self, other): |
| 61 | return (self.netfn == other.netfn) and (self.cmd == other.cmd) |
| 62 | |
| 63 | def parse(config): |
| 64 | entries = [] |
| 65 | with open(config) as f: |
| 66 | for line in f: |
| 67 | line = line.strip() |
| 68 | if len(line) == 0 or line[0] == '#': |
| 69 | continue |
| 70 | e = entry(line) |
| 71 | if any([e.match(item) for item in entries]): |
| 72 | d = DuplicateEntry(e) |
| 73 | sys.stderr.write("WARNING: {}\n".format(d)) |
| 74 | else: |
| 75 | entries.append(e) |
| 76 | entries.sort() |
| 77 | return entries |
| 78 | |
| 79 | def output(entries, hppfile): |
| 80 | lines = [ |
| 81 | "#pragma once", |
| 82 | "", |
| 83 | "// AUTOGENERATED FILE; DO NOT MODIFY", |
| 84 | "", |
| 85 | "#include <array>", |
| 86 | "#include <tuple>", |
| 87 | "", |
| 88 | "using netfncmd_tuple = std::tuple<unsigned char, unsigned char, unsigned short>;", |
| 89 | "", |
| 90 | "constexpr const std::array<netfncmd_tuple, {}> whitelist = ".format( |
| 91 | len(entries)), |
| 92 | "{{" |
| 93 | ] |
| 94 | lines.extend([' {}'.format(e) for e in entries]) |
| 95 | lines.append("}};\n"); |
| 96 | |
| 97 | with open(hppfile, "w") as hpp: |
| 98 | hpp.write("\n".join(lines)) |
| 99 | |
| 100 | |
| 101 | if __name__ == "__main__": |
| 102 | if len(sys.argv) != 3: |
| 103 | usage() |
| 104 | config = sys.argv[1] |
| 105 | header = sys.argv[2] |
| 106 | entries = parse(config) |
| 107 | output(entries, header) |