view shasum.py @ 6:a21e83c855cc

Help message: drop "yet" for text-mode flag
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 04 Dec 2020 13:15:53 +0100
parents bbcb225640de
children 47b4c98e4d40
line wrap: on
line source

r"""
:Author:    Franz Glasner
:Copyright: (c) 2020 Franz Glasner.
            All rights reserved.
:License:   BSD 3-Clause "New" or "Revised" License.
            See :ref:`LICENSE <license>` for details.
            If you cannot find LICENSE see
            <https://opensource.org/licenses/BSD-3-Clause>
:ID:        @(#) HGid$

"""

from __future__ import print_function

import argparse
import hashlib
import sys


PY2 = sys.version_info[0] < 3

CHUNK_SIZE = 1024 * 1024 * 1024


def main(argv=None):
    aparser = argparse.ArgumentParser(
        description="Python implementation of shasum",
        fromfile_prefix_chars='@')
    aparser.add_argument(
        "--binary", "-b", action="store_false", dest="text_mode", default=False,
        help="read in binary mode (default)")
    aparser.add_argument(
        "--bsd", "-B", action="store_true",
        help="write BSD style output")
    aparser.add_argument(
        "--text", "-t", action="store_true", dest="text_mode", default=False,
        help="read in text mode (not supported)")
    aparser.add_argument(
        "files", nargs="*", metavar="FILE")

    opts = aparser.parse_args(args=argv)

    if opts.text_mode:
        print("ERROR: text mode not supported", file=sys.stderr)
        sys.exit(78)   # :manpage:`sysexits(3)` EX_CONFIG

    if opts.bsd:
        out = out_bsd
    else:
        out = out_std

    if not opts.files:
        opts.files.append('-')
    if len(opts.files) == 1 and opts.files[0] == '-':
        if PY2:
            if sys.platform == "win32":
                import os. msvcrt
                msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
            source = sys.stdin
        else:
            source = sys.stdin.buffer
        out(sys.stdout,
            compute_digest(hashlib.sha256, source),
            None,
            "SHA256",
            True)
    else:
        for fn in opts.files:
            with open(fn, "rb") as source:
                out(sys.stdout,
                    compute_digest(hashlib.sha256, source),
                    fn,
                    "SHA256",
                    True)


def out_bsd(dest, digest, filename, digestname, binary):
    if filename is None:
        print(digest, file=dest)
    else:
        print("{} ({}) = {}".format(digestname, filename, digest),
              file=dest)


def out_std(dest, digest, filename, digestname, binary):
    print("{} {}{}".format(digest,
                           '*' if binary else ' ',
                           '-' if filename is None else filename),
          file=dest)


def compute_digest(hashobj, instream):
    """

    :param hashobj: a :mod:`hashlib` compatible hash algorithm type or factory
    :param instream: a bytes input stream to read the data to be hashed from
    :return: the digest in hex form
    :rtype: str

    """
    h = hashobj()
    while True:
        buf = instream.read(CHUNK_SIZE)
        if buf is not None:
            if len(buf) == 0:
                break
            h.update(buf)
    return h.hexdigest()


if __name__ == "__main__":
    sys.exit(main())