#!/usr/bin/env python3 """plutil(1) shim backed by Python's plistlib. MakeInc.def hard-codes PLUTIL = /usr/bin/plutil and MakeInc.kernel / MakeInc.rule use it in three shapes: plutil -convert xml1|binary1|json [-o OUT] IN plutil -lint IN plutil -extract KEYPATH KIND [-o OUT] IN (rare) Anything else is refused loudly rather than silently succeeding. """ import plistlib import sys FMT = {"xml1": plistlib.FMT_XML, "binary1": plistlib.FMT_BINARY} def read(path): if path == "-": return plistlib.loads(sys.stdin.buffer.read()) with open(path, "rb") as fh: return plistlib.load(fh) def write(obj, path, fmt): data = plistlib.dumps(obj, fmt=fmt, sort_keys=True) if path in (None, "-"): sys.stdout.buffer.write(data) else: with open(path, "wb") as fh: fh.write(data) def main(argv): out = None mode = None conv = None extract = None files = [] i = 0 while i < len(argv): a = argv[i] if a == "-convert": mode, conv = "convert", argv[i + 1] i += 2 elif a == "-o": out = argv[i + 1] i += 2 elif a == "-lint": mode = "lint" i += 1 elif a == "-extract": mode, extract = "extract", (argv[i + 1], argv[i + 2]) i += 3 elif a in ("-s", "-r", "-p", "--"): i += 1 elif a.startswith("-"): print("plutil: unsupported option %s (UniXNU shim)" % a, file=sys.stderr) return 1 else: files.append(a) i += 1 if not files: print("plutil: no input file", file=sys.stderr) return 1 rc = 0 for path in files: try: obj = read(path) except Exception as exc: # noqa: BLE001 print("%s: %s" % (path, exc), file=sys.stderr) rc = 1 continue if mode == "lint": print("%s: OK" % path) elif mode == "convert": if conv == "json": import json data = json.dumps(obj).encode() if out in (None, "-"): sys.stdout.buffer.write(data) else: open(out, "wb").write(data) elif conv in FMT: write(obj, out if out else path, FMT[conv]) else: print("plutil: unknown format %s" % conv, file=sys.stderr) rc = 1 elif mode == "extract": keypath, kind = extract cur = obj for part in keypath.split("."): cur = cur[part] if kind == "raw": print(cur) else: write(cur, out, FMT.get(kind, plistlib.FMT_XML)) else: write(obj, out, plistlib.FMT_XML) return rc if __name__ == "__main__": sys.exit(main(sys.argv[1:]))