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() |