diff options
| -rwxr-xr-x | scripts/xattr_user_crc32 | 84 | 
1 files changed, 84 insertions, 0 deletions
diff --git a/scripts/xattr_user_crc32 b/scripts/xattr_user_crc32 new file mode 100755 index 0000000..a13840f --- /dev/null +++ b/scripts/xattr_user_crc32 @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +# Copyright 2020 vg +# SPDX-License-Identifier: MIT + +''' +Description +=========== + +Tag given files with their crc32 and display the tagged crc32 then the new +crc32 and then the filename to stdout. + +Usage +===== + +Usage: {progname} [-z] [--] [FILENAME...] +       {progname} -h|--help + +Options: + +  -h, --help +        Show the complete help + +  -z +        Null instead of LF as filename separator for in and out +''' + +### standard modules +import contextlib +import functools +import itertools +import operator +import os +import sys + +### external modules +import docopt + + +# This script is not compatible below python3.7.2, always abort (not in main +# since the syntax itself can cause the script to fail later inconveniently). +assert sys.hexversion >= 0x03070200 + + +def stream_split(sep='\n', stream=sys.stdin, *, rem='', chunksize=4096): +    if stream.isatty(): +        # workaround the double Ctrl-D issue for interactive entry where perf +        # should not be an issue thus reading 1 by 1 is ok. +        chunksize = 1 +    for data in iter(functools.partial(stream.read, chunksize), ''): +        splitted = ''.join((rem, data)).split(sep) +        for part in itertools.islice(splitted, len(splitted)-1): +            yield part +        rem = splitted[-1] +    yield rem + + +def filenames(arg_filenames, separator): +    yield from filter(bool, arg_filenames or stream_split(separator)) + + +def get_crc32(filename): +    from zlib import crc32 +    with open(filename, 'rb') as stream: +        return '%08X' % functools.reduce( +                lambda a, b: crc32(b, a), +                iter(functools.partial(stream.read, 4096), b''), +                0 +        ) + + +def main(): +    args = docopt.docopt(__doc__.format(progname=os.path.basename(sys.argv[0]))) +    separator = '\0' if args['-z'] else '\n' +    for filename in filenames(args['FILENAME'], separator): +        tag_crc32 = None +        with contextlib.suppress(OSError): +            tag_crc32 = os.getxattr(filename, 'user.crc32').decode('utf8') +        calculated_crc32 = get_crc32(filename) +        os.setxattr(filename, 'user.crc32', calculated_crc32.encode('utf8')) +        print('%-8s %-8s %s' +                % (tag_crc32, calculated_crc32, filename), end=separator) + + +main()  | 
