Mercurial > hgrepos > Python > apps > py-cutils
diff shasum.py @ 22:6bdfc5ad4656
Implemented OpenBSD's -C (aka --checklist) option for shasum
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Sat, 05 Dec 2020 15:08:46 +0100 |
| parents | f2d634270e1c |
| children | 232063b73e45 |
line wrap: on
line diff
--- a/shasum.py Sat Dec 05 13:32:00 2020 +0100 +++ b/shasum.py Sat Dec 05 15:08:46 2020 +0100 @@ -51,6 +51,12 @@ the comparison. This will validate any of the supported checksums. If no file is given, stdin is used.""") aparser.add_argument( + "--checklist", "-C", metavar="CHECKLIST", + help="""Compare the checksum of each FILE against the checksums in +the CHECKLIST. Any specified FILE that is not listed in the CHECKLIST will +generate an error.""") + + aparser.add_argument( "--reverse", "-r", action="store_false", dest="bsd", default=False, help="explicitely select normal coreutils style output (to be option compatible with BSD style commands and :command:`openssl dgst -r`)") aparser.add_argument( @@ -68,13 +74,20 @@ if opts.text_mode: print("ERROR: text mode not supported", file=sys.stderr) - sys.exit(78) # :manpage:`sysexits(3)` EX_CONFIG + sys.exit(78) # :manpage:`sysexits(3)` EX_CONFIG + + if opts.check and opts.checklist: + print("ERROR: only one of --check or --checklist allowed", + file=sys.stderr) + sys.exit(64) # :manpage:`sysexits(3)` EX_USAGE if not opts.algorithm: opts.algorithm = argv2algo("1") if opts.check: return verify_digests_from_files(opts) + elif opts.checklist: + return verify_digests_with_checklist(opts) else: return generate_digests(opts) @@ -108,6 +121,48 @@ return 0 +def verify_digests_with_checklist(opts): + exit_code = 0 + if not opts.files or (len(opts.files) == 1 and opts.files[0] == '-'): + if PY2: + if sys.platform == "win32": + import os, msvcrt # noqa: E401 + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + source = sys.stdin + else: + source = sys.stdin.buffer + pl = get_parsed_digest_line_from_checklist(opts.checklist, opts, None) + if pl is None: + exit_code = 1 + print("-: MISSING") + else: + tag, algo, cl_filename, cl_digest = pl + computed_digest = compute_digest(algo, source) + if cl_digest.lower() == computed_digest.lower(): + res = "OK" + else: + res = "FAILED" + exit_code = 1 + print("{}: {}: {}".format(tag, "-", res)) + else: + for fn in opts.files: + pl = get_parsed_digest_line_from_checklist(opts.checklist, opts, fn) + if pl is None: + print("{}: MISSING".format(fn)) + exit_code = 1 + else: + tag, algo, cl_filename, cl_digest = pl + with open(fn, "rb") as source: + computed_digest = compute_digest(algo, source) + if cl_digest.lower() == computed_digest.lower(): + res = "OK" + else: + exit_code = 1 + res = "FAILED" + print("{}: {}: {}".format(tag, fn, res)) + return exit_code + + def verify_digests_from_files(opts): exit_code = 0 if not opts.files or (len(opts.files) == 1 and opts.files[0] == '-'): @@ -154,6 +209,27 @@ return ("missing", fn, tag) +def get_parsed_digest_line_from_checklist(checklist, opts, filename): + if filename is None: + filenames = ("-", "stdin", "", ) + else: + filenames = ( + normalize_filename(filename, strip_leading_dot_slash=True),) + with io.open(checklist, "rt", encoding="utf-8") as clf: + for checkline in clf: + if not checkline: + continue + parts = parse_digest_line(opts, checkline) + if not parts: + raise ValueError( + "improperly formatted digest line: {}".format(checkline)) + fn = normalize_filename(parts[2], strip_leading_dot_slash=True) + if fn in filenames: + return parts + else: + return None + + def parse_digest_line(opts, line): """Parse a `line` of a digest file and return its parts.
