changeset 330:61cbae10103c

treesum: first minimal version of writing the treesum output in tabular format. BUGS: Reading tabular output is not yet supported.
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 28 Mar 2025 14:20:45 +0100
parents e2ae72792f56
children 9ee84624587f
files cutils/treesum.py
diffstat 1 files changed, 114 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/cutils/treesum.py	Wed Mar 26 23:44:30 2025 +0100
+++ b/cutils/treesum.py	Fri Mar 28 14:20:45 2025 +0100
@@ -167,9 +167,10 @@
                  "If not given or if it is given as `-' then stdout is used.")
         gp.add_argument(
             "--output-style", dest="output_style", default="tagged",
-            choices=("tagged", "tag"),
+            choices=("tagged", "tag", "tabular", "tab"),
             help="""
-Select the output style: "tagged" or "tag" selects BSD style tagged format.
+Select the output style: "tagged" or "tag" selects a more BSD style tagged
+format. "tabular" or "tab" select a more GNU style tabular format.
 Default is "tagged".
 """)
         gp.add_argument(
@@ -456,7 +457,7 @@
                 )
     if generator not in ("normal", "full", "none"):
         raise ValueError("given generator `%s' not allowed" % (generator, ))
-    if output_style not in ("tagged", "tag"):
+    if output_style not in ("tagged", "tag", "tabular", "tab"):
         raise ValueError(
             "given output_style `%s' not allowed" % (output_style,))
 
@@ -530,10 +531,15 @@
     fnmatcher = fnmatch.FnMatcher.build_from_commandline_patterns(
         opts.fnmatch_filters)
 
-    assert opts.output_style in ("tagged", "tag")
+    if opts.output_style in ("tagged", "tag"):
+        writerstyle = TaggedTreesumWriter
+    elif opts.output_style in ("tabular", "tab"):
+        writerstyle = TabularTreesumWriter
+    else:
+        raise NotImplementedError("`output_style'")
 
     with out_cm as outfp:
-        writer = TaggedTreesumWriter(outfp)
+        writer = writerstyle(outfp)
         for d in opts.directories:
             V1DirectoryTreesumGenerator(
                 opts.algorithm, opts.mmap, opts.base64,
@@ -1441,6 +1447,109 @@
         self.flush()
 
 
+class TabularTreesumWriter(WriterBase):
+
+    """Writer to write treesum digest files in a format similar to tabular
+    GNU digest files.
+
+    Provides high-level methods to write data lines.
+
+    """
+
+    def __init__(self, outfp, **kwds):
+        super(TabularTreesumWriter, self).__init__(outfp)
+
+    def start(self, version):
+        """Begin a new block, reset the current CRC and write the VERSION
+        tag.
+
+        """
+        self.reset_crc()
+        self.write(b"VERSION\t")
+        self.writeln(util.b(version))
+
+    def write_comment(self, comment):
+        self.write(b"COMMENT\t")
+        self.writeln(util.b(comment, "utf-8"))
+
+    def write_generator(self, generator):
+        self.write(b"GENERATOR\t")
+        self.writeln(util.b(generator, "utf-8"))
+
+    def write_error(self, error):
+        self.write(b"ERROR\t")
+        self.writeln(util.b(error, "utf-8"))
+
+    def write_fsencoding(self, encoding):
+        self.write(b"FSENCODING\t")
+        self.writeln(util.b(encoding))
+
+    def write_fnmatch_pattern(self, action, kind, pattern):
+        self.write(b"FNMATCH\t")
+        self.write(util.b(action))
+        self.write(b": ")
+        self.write(util.b(kind))
+        self.write(b":")
+        self.writeln(util.b(pattern, "utf-8"))
+
+    def write_flags(self, flags):
+        self.write(b"FLAGS\t")
+        if isinstance(flags, (str, bytes)):
+            self.writeln(util.b(flags))
+        else:
+            flags.sort()
+            self.writeln(util.b(",".join(flags)))
+
+    def write_timestamp(self, ts):
+        self.write(b"TIMESTAMP\t")
+        self.writeln(util.b(str(ts)))
+
+    def write_isotimestamp(self, ts):
+        self.write(b"ISOTIMESTAMP\t")
+        self.writeln(util.b(ts))
+
+    def write_root(self, root):
+        assert isinstance(root, bytes)
+        self.write(b"ROOT\t")
+        self.writeln(root)
+
+    def write_size(self, filename, sz):
+        assert isinstance(filename, bytes)
+        if sz is not None:
+            self.write(util.b(str(sz)))
+        self.write(b"\t")
+        self.writeln(filename)
+
+    def write_accept_treesum_file(self, filename):
+        assert isinstance(filename, bytes)
+        self.write(b"ACCEPT-TREESUM\t")
+        self.writeln(filename)
+
+    def write_file_digest(self, algorithm, filename, digest,
+                          use_base64=False, size=None):
+        assert isinstance(filename, bytes)
+        if digest is not None:
+            digest = (base64.b64encode(digest)
+                      if use_base64
+                      else binascii.hexlify(digest))
+        self.write(util.b(algorithm))
+        self.write(b":")
+        if digest is not None:
+            self.write(digest)
+        self.write(b"\t")
+        if size is not None:
+            self.write(util.b(str(size)))
+            self.write(b"\t")
+        self.writeln(filename)
+
+    def finish(self):
+        """Finish a block and write the current CRC"""
+        crc = self.crc.hexdigest()
+        self.write(b"CRC32\t")
+        self.writeln(util.b(crc))
+        self.flush()
+
+
 class TreesumReader(object):
 
     """Reader to read and/or verify treesum digest files.