1import argparse 2import struct 3 4def read_entry(f) -> dict: 5 name = struct.unpack_from("12s", f.read(12))[0] 6 uid = struct.unpack_from(">I", f.read(4))[0] 7 gid = struct.unpack_from(">H", f.read(2))[0] 8 is_file = struct.unpack_from("?", f.read(1))[0] 9 modes = struct.unpack_from("BBB", f.read(3)) 10 attr = struct.unpack_from("B", f.read(2))[0] 11 x3 = struct.unpack_from(">I", f.read(4))[0] 12 num_children = struct.unpack_from(">I", f.read(4))[0] 13 14 children = [] 15 for i in range(num_children): 16 children.append(read_entry(f)) 17 18 return { 19 "name": name, 20 "uid": uid, 21 "gid": gid, 22 "is_file": is_file, 23 "modes": modes, 24 "attr": attr, 25 "x3": x3, 26 "children": children, 27 } 28 29COLOR_RESET = "\x1b[0;00m" 30BOLD = "\x1b[0;37m" 31COLOR_BLUE = "\x1b[1;34m" 32COLOR_GREEN = "\x1b[0;32m" 33 34def print_entry(entry, indent) -> None: 35 mode_str = {0: "--", 1: "r-", 2: "-w", 3: "rw"} 36 37 sp = ' ' * indent 38 color = BOLD if entry["is_file"] else COLOR_BLUE 39 40 owner = f"{COLOR_GREEN}{entry['uid']:04x}{COLOR_RESET}:{entry['gid']:04x}" 41 attrs = f"{''.join(mode_str[mode] for mode in entry['modes'])}" 42 other_attrs = f"{entry['attr']} {entry['x3']}" 43 44 print(f"{sp}{color}{entry['name'].decode()}{COLOR_RESET} [{owner} {attrs} {other_attrs}]") 45 for child in entry["children"]: 46 print_entry(child, indent + 2) 47 48def main() -> None: 49 parser = argparse.ArgumentParser(description="Prints a FST in a tree-like format.") 50 parser.add_argument("file") 51 args = parser.parse_args() 52 53 with open(args.file, "rb") as f: 54 root = read_entry(f) 55 56 print_entry(root, 0) 57 58main() 59