diff cutils/treesum.py @ 170:8945be6b404e

Mode for treesum.py to print only the size of files and the accumulated size of a directory: --size-only. Digests are not computed.
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 10 Jan 2025 10:32:52 +0100
parents 91b8b2a8aebc
children 804a823c63f5
line wrap: on
line diff
--- a/cutils/treesum.py	Thu Jan 09 18:11:38 2025 +0100
+++ b/cutils/treesum.py	Fri Jan 10 10:32:52 2025 +0100
@@ -114,6 +114,10 @@
 The size is not considered when computing digests. For symbolic links
 the size is not printed also.""")
         gp.add_argument(
+            "--size-only", action="store_true",
+            help="""Print only the size of files and for each directory its
+accumulated directory size. Digests are not computed.""")
+        gp.add_argument(
             "directories", nargs="*", metavar="DIRECTORY")
 
     parser = argparse.ArgumentParser(
@@ -213,7 +217,8 @@
                       mmap=None,
                       mtime=False,
                       output=None,
-                      print_size=False):
+                      print_size=False,
+                      size_only=False):
     opts = argparse.Namespace(
         directories=directories,
         algorithm=(util.algotag2algotype(algorithm),
@@ -229,7 +234,8 @@
         metadata_mode=mode,
         metadata_mtime=mtime,
         output=output,
-        print_size=print_size)
+        print_size=print_size,
+        size_only=size_only)
     return opts
 
 
@@ -268,6 +274,7 @@
                 opts.metadata_mode,
                 opts.metadata_full_mode,
                 opts.metadata_mtime,
+                opts.size_only,
                 opts.print_size,
                 minimal=opts.minimal,
                 comment=opts.comment)
@@ -276,7 +283,7 @@
 def generate_treesum_for_directory(
         outfp, root, algorithm, use_mmap, use_base64, handle_root_logical,
         follow_directory_symlinks, with_metadata_mode, with_metadata_full_mode,
-        with_metadata_mtime, print_size,
+        with_metadata_mtime, size_only, print_size,
         minimal=None, comment=None):
     """
 
@@ -298,8 +305,11 @@
         flags.append("logical")
     if follow_directory_symlinks:
         flags.append("follow-directory-symlinks")
-    if print_size:
-        flags.append("print-size")
+    if size_only:
+        flags.append("size-only")
+    else:
+        if print_size:
+            flags.append("print-size")
     if flags:
         flags.sort()
         outfp.write(format_bsd_line("FLAGS", ",".join(flags), None, False))
@@ -335,12 +345,21 @@
         dir_dgst.update(b"1:L,")
         dir_dgst.update(
             b"%d:%s," % (len(linkdgst.digest()), linkdgst.digest()))
-        outfp.write(
-            format_bsd_line(
-                algorithm[1],
-                dir_dgst.digest(),
-                "./@",
-                use_base64))
+        if size_only:
+            outfp.write(
+                format_bsd_line(
+                    "SIZE",
+                    None,
+                    "./@",
+                    False,
+                    0))
+        else:
+            outfp.write(
+                format_bsd_line(
+                    algorithm[1],
+                    dir_dgst.digest(),
+                    "./@",
+                    use_base64))
         outfp.flush()
         return
 
@@ -363,20 +382,28 @@
                         b"%d:%s,"
                         % (len(linkdgst.digest()), linkdgst.digest()))
                     opath = "/".join(top) + "/" + fso.name if top else fso.name
-                    outfp.write(
-                        format_bsd_line(
-                            algorithm[1],
-                            linkdgst.digest(),
-                            "%s/./@" % (opath,),
-                            use_base64))
+                    if size_only:
+                        outfp.write(
+                            format_bsd_line(
+                                "SIZE",
+                                None,
+                                "%s/./@" % (opath,),
+                                False,
+                                0))
+                    else:
+                        outfp.write(
+                            format_bsd_line(
+                                algorithm[1],
+                                linkdgst.digest(),
+                                "%s/./@" % (opath,),
+                                use_base64))
                     outfp.flush()
                     continue
                 # fetch from dir_digests
                 dgst, dsz = dir_digests[top + (fso.name,)]
                 dir_size += dsz
                 dir_dgst.update(b"1:d,%d:%s," % (len(fso.fsname), fso.fsname))
-                dir_dgst.update(
-                    b"%d:%s," % (len(dgst), dgst))
+                dir_dgst.update(b"%d:%s," % (len(dgst), dgst))
                 if with_metadata_full_mode:
                     modestr = normalized_mode_str(fso.stat.st_mode)
                     if not isinstance(modestr, bytes):
@@ -409,27 +436,38 @@
                     if not isinstance(modestr, bytes):
                         modestr = modestr.encode("ascii")
                     dir_dgst.update(b"4:mode,%d:%s," % (len(modestr), modestr))
-                dgst = digest.compute_digest_file(
-                    algorithm[0], fso.path, use_mmap=use_mmap)
-                dir_dgst.update(b"%d:%s," % (len(dgst), dgst))
+                if not size_only:
+                    dgst = digest.compute_digest_file(
+                        algorithm[0], fso.path, use_mmap=use_mmap)
+                    dir_dgst.update(b"%d:%s," % (len(dgst), dgst))
                 opath = "/".join(top) + "/" + fso.name if top else fso.name
-                if print_size:
+                if size_only:
                     outfp.write(
                         format_bsd_line(
-                            algorithm[1], dgst, opath, use_base64,
-                            fso.stat.st_size))
+                            "SIZE", None, opath, False, fso.stat.st_size))
                 else:
-                    outfp.write(
-                        format_bsd_line(
-                            algorithm[1], dgst, opath, use_base64))
+                    if print_size:
+                        outfp.write(
+                            format_bsd_line(
+                                algorithm[1], dgst, opath, use_base64,
+                                fso.stat.st_size))
+                    else:
+                        outfp.write(
+                            format_bsd_line(
+                                algorithm[1], dgst, opath, use_base64))
                 outfp.flush()
         opath = "/".join(top) + "/" if top else ""
-        if print_size:
+        if size_only:
             outfp.write(format_bsd_line(
-                algorithm[1], dir_dgst.digest(), opath, use_base64, dir_size))
+                    "SIZE", None, opath, False, dir_size))
         else:
-            outfp.write(format_bsd_line(
-                algorithm[1], dir_dgst.digest(), opath, use_base64))
+            if print_size:
+                outfp.write(format_bsd_line(
+                    algorithm[1], dir_dgst.digest(), opath,
+                    use_base64, dir_size))
+            else:
+                outfp.write(format_bsd_line(
+                    algorithm[1], dir_dgst.digest(), opath, use_base64))
         outfp.flush()
         dir_digests[top] = (dir_dgst.digest(), dir_size)
 
@@ -450,28 +488,30 @@
     return modestr
 
 
-def format_bsd_line(digestname, value, filename, use_base64, size=None):
+def format_bsd_line(what, value, filename, use_base64, size=None):
     ls = os.linesep if isinstance(os.linesep, bytes) \
         else os.linesep.encode("utf-8")
-    if not isinstance(digestname, bytes):
-        digestname = digestname.encode("ascii")
-    if digestname == b"TIMESTAMP":
+    if not isinstance(what, bytes):
+        what = what.encode("ascii")
+    if what == b"TIMESTAMP":
         assert filename is None
         return b"TIMESTAMP = %d%s" % (value, ls)
-    if digestname in (b"ISOTIMESTAMP", b"FLAGS", b"VERSION"):
+    if what in (b"ISOTIMESTAMP", b"FLAGS", b"VERSION"):
         assert filename is None
         if not isinstance(value, bytes):
             value = value.encode("ascii")
-        return b"%s = %s%s" % (digestname, value, ls)
+        return b"%s = %s%s" % (what, value, ls)
     assert filename is not None
-    if digestname == b"COMMENT":
+    if what == b"COMMENT":
         if not isinstance(filename, bytes):
             filename = filename.encode("utf-8")
         return b"COMMENT (%s)%s" % (filename, ls)
     if not isinstance(filename, bytes):
         filename = util.fsencode(filename)
+    if what == b"SIZE":
+        return b"SIZE (%s) = %d%s" % (filename, size, ls)
     if value is None:
-        return b"%s (%s)%s" % (digestname, filename, ls)
+        return b"%s (%s)%s" % (what, filename, ls)
     if use_base64:
         value = base64.b64encode(value)
     else:
@@ -479,9 +519,9 @@
     if filename != b"./@":
         filename = util.normalize_filename(filename, True)
     if size is None:
-        return b"%s (%s) = %s%s" % (digestname, filename, value, ls)
+        return b"%s (%s) = %s%s" % (what, filename, value, ls)
     else:
-        return b"%s (%s) = %s,%d%s" % (digestname, filename, value, size, ls)
+        return b"%s (%s) = %s,%d%s" % (what, filename, value, size, ls)
 
 
 if __name__ == "__main__":